diff --git a/app/actions/definitions/apiKeys.tsx b/app/actions/definitions/apiKeys.tsx
new file mode 100644
index 000000000..0252f1086
--- /dev/null
+++ b/app/actions/definitions/apiKeys.tsx
@@ -0,0 +1,25 @@
+import { PlusIcon } from "outline-icons";
+import * as React from "react";
+import stores from "~/stores";
+import APIKeyNew from "~/scenes/APIKeyNew";
+import { createAction } from "..";
+import { SettingsSection } from "../sections";
+
+export const createApiKey = createAction({
+ name: ({ t }) => t("New API key"),
+ analyticsName: "New API key",
+ section: SettingsSection,
+ icon: ,
+ keywords: "create",
+ visible: () =>
+ stores.policies.abilities(stores.auth.team?.id || "").createApiKey,
+ perform: ({ t, event }) => {
+ event?.preventDefault();
+ event?.stopPropagation();
+
+ stores.dialogs.openModal({
+ title: t("New API key"),
+ content: ,
+ });
+ },
+});
diff --git a/app/hooks/useSettingsConfig.ts b/app/hooks/useSettingsConfig.ts
index 1183acd95..482a100f1 100644
--- a/app/hooks/useSettingsConfig.ts
+++ b/app/hooks/useSettingsConfig.ts
@@ -88,7 +88,7 @@ const useSettingsConfig = () => {
icon: EmailIcon,
},
{
- name: t("API Tokens"),
+ name: t("API"),
path: settingsPath("tokens"),
component: ApiKeys,
enabled: can.createApiKey,
diff --git a/app/scenes/APITokenNew.tsx b/app/scenes/APIKeyNew.tsx
similarity index 69%
rename from app/scenes/APITokenNew.tsx
rename to app/scenes/APIKeyNew.tsx
index 96967eae0..68125cc69 100644
--- a/app/scenes/APITokenNew.tsx
+++ b/app/scenes/APIKeyNew.tsx
@@ -1,6 +1,7 @@
import * as React from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
+import { ApiKeyValidation } from "@shared/validations";
import Button from "~/components/Button";
import Flex from "~/components/Flex";
import Input from "~/components/Input";
@@ -11,7 +12,7 @@ type Props = {
onSubmit: () => void;
};
-function APITokenNew({ onSubmit }: Props) {
+function APIKeyNew({ onSubmit }: Props) {
const [name, setName] = React.useState("");
const [isSaving, setIsSaving] = React.useState(false);
const { apiKeys } = useStores();
@@ -26,7 +27,7 @@ function APITokenNew({ onSubmit }: Props) {
await apiKeys.create({
name,
});
- toast.success(t("API token created"));
+ toast.success(t("API Key created"));
onSubmit();
} catch (err) {
toast.error(err.message);
@@ -45,7 +46,7 @@ function APITokenNew({ onSubmit }: Props) {
);
}
-export default APITokenNew;
+export default APIKeyNew;
diff --git a/app/scenes/Settings/ApiKeys.tsx b/app/scenes/Settings/ApiKeys.tsx
index 52e30694f..000a3faa2 100644
--- a/app/scenes/Settings/ApiKeys.tsx
+++ b/app/scenes/Settings/ApiKeys.tsx
@@ -3,15 +3,14 @@ import { CodeIcon } from "outline-icons";
import * as React from "react";
import { useTranslation, Trans } from "react-i18next";
import ApiKey from "~/models/ApiKey";
-import APITokenNew from "~/scenes/APITokenNew";
import { Action } from "~/components/Actions";
import Button from "~/components/Button";
import Heading from "~/components/Heading";
-import Modal from "~/components/Modal";
import PaginatedList from "~/components/PaginatedList";
import Scene from "~/components/Scene";
import Text from "~/components/Text";
-import useBoolean from "~/hooks/useBoolean";
+import { createApiKey } from "~/actions/definitions/apiKeys";
+import useActionContext from "~/hooks/useActionContext";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
@@ -21,12 +20,12 @@ function ApiKeys() {
const team = useCurrentTeam();
const { t } = useTranslation();
const { apiKeys } = useStores();
- const [newModalOpen, handleNewModalOpen, handleNewModalClose] = useBoolean();
const can = usePolicy(team);
+ const context = useActionContext();
return (
}
actions={
<>
@@ -34,19 +33,20 @@ function ApiKeys() {
)}
>
}
>
- {t("API Tokens")}
+ {t("API Keys")}developer documentation."
components={{
em: (
@@ -67,13 +67,6 @@ function ApiKeys() {
)}
/>
-
-
-
);
}
diff --git a/server/models/ApiKey.ts b/server/models/ApiKey.ts
index 6b6a06398..f75833dd7 100644
--- a/server/models/ApiKey.ts
+++ b/server/models/ApiKey.ts
@@ -8,6 +8,7 @@ import {
BelongsTo,
ForeignKey,
} from "sequelize-typescript";
+import { ApiKeyValidation } from "@shared/validations";
import User from "./User";
import ParanoidModel from "./base/ParanoidModel";
import Fix from "./decorators/Fix";
@@ -22,9 +23,9 @@ class ApiKey extends ParanoidModel<
static prefix = "ol_api_";
@Length({
- min: 3,
- max: 255,
- msg: "Name must be between 3 and 255 characters",
+ min: ApiKeyValidation.minNameLength,
+ max: ApiKeyValidation.maxNameLength,
+ msg: `Name must be between ${ApiKeyValidation.minNameLength} and ${ApiKeyValidation.maxNameLength} characters`,
})
@Column
name: string;
diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json
index c41920f97..389c7c137 100644
--- a/shared/i18n/locales/en_US/translation.json
+++ b/shared/i18n/locales/en_US/translation.json
@@ -1,4 +1,5 @@
{
+ "New API key": "New API key",
"Open collection": "Open collection",
"New collection": "New collection",
"Create a collection": "Create a collection",
@@ -431,7 +432,7 @@
"Could not import file": "Could not import file",
"Unsubscribed from document": "Unsubscribed from document",
"Account": "Account",
- "API Tokens": "API Tokens",
+ "API": "API",
"Details": "Details",
"Security": "Security",
"Features": "Features",
@@ -495,8 +496,8 @@
"left a comment on": "left a comment on",
"shared": "shared",
"invited you to": "invited you to",
- "API token created": "API token created",
- "Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".": "Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".",
+ "API Key created": "API Key created",
+ "Name your key something that will help you to remember it's use in the future, for example \"local development\" or \"continuous integration\".": "Name your key something that will help you to remember it's use in the future, for example \"local development\" or \"continuous integration\".",
"The document archive is empty at the moment.": "The document archive is empty at the moment.",
"Collection menu": "Collection menu",
"Drop documents to import": "Drop documents to import",
@@ -755,10 +756,9 @@
"We were unable to find the page you’re looking for.": "We were unable to find the page you’re looking for.",
"Search titles only": "Search titles only",
"No documents found for your search filters.": "No documents found for your search filters.",
- "New token": "New token",
- "You can create an unlimited amount of personal tokens to authenticate\n with the API. Tokens have the same permissions as your user account.\n For more details see the developer documentation.": "You can create an unlimited amount of personal tokens to authenticate\n with the API. Tokens have the same permissions as your user account.\n For more details see the developer documentation.",
+ "API Keys": "API Keys",
+ "Create personal API keys to authenticate with the API and programatically control\n your workspace's data. API keys have the same permissions as your user account.\n For more details see the developer documentation.": "Create personal API keys to authenticate with the API and programatically control\n your workspace's data. API keys have the same permissions as your user account.\n For more details see the developer documentation.",
"Active": "Active",
- "Create a token": "Create a token",
"API token copied to clipboard": "API token copied to clipboard",
"Copied": "Copied",
"Revoking": "Revoking",
diff --git a/shared/validations.ts b/shared/validations.ts
index c6a7a7056..2110bddc4 100644
--- a/shared/validations.ts
+++ b/shared/validations.ts
@@ -19,6 +19,13 @@ export const AttachmentValidation = {
],
};
+export const ApiKeyValidation = {
+ /** The minimum length of the API key name */
+ minNameLength: 3,
+ /** The maximum length of the API key name */
+ maxNameLength: 255,
+};
+
export const CollectionValidation = {
/** The maximum length of the collection description */
maxDescriptionLength: 10 * 1000,