chore: Tidy API key settings page
This commit is contained in:
25
app/actions/definitions/apiKeys.tsx
Normal file
25
app/actions/definitions/apiKeys.tsx
Normal file
@@ -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: <PlusIcon />,
|
||||
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: <APIKeyNew onSubmit={stores.dialogs.closeAllModals} />,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -88,7 +88,7 @@ const useSettingsConfig = () => {
|
||||
icon: EmailIcon,
|
||||
},
|
||||
{
|
||||
name: t("API Tokens"),
|
||||
name: t("API"),
|
||||
path: settingsPath("tokens"),
|
||||
component: ApiKeys,
|
||||
enabled: can.createApiKey,
|
||||
|
||||
@@ -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) {
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Text as="p" type="secondary">
|
||||
{t(
|
||||
`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 key something that will help you to remember it's use in the future, for example "local development" or "continuous integration".`
|
||||
)}
|
||||
</Text>
|
||||
<Flex>
|
||||
@@ -54,16 +55,20 @@ function APITokenNew({ onSubmit }: Props) {
|
||||
label={t("Name")}
|
||||
onChange={handleNameChange}
|
||||
value={name}
|
||||
minLength={ApiKeyValidation.minNameLength}
|
||||
maxLength={ApiKeyValidation.maxNameLength}
|
||||
required
|
||||
autoFocus
|
||||
flex
|
||||
/>
|
||||
</Flex>
|
||||
<Button type="submit" disabled={isSaving || !name}>
|
||||
{isSaving ? `${t("Creating")}…` : t("Create")}
|
||||
</Button>
|
||||
<Flex justify="flex-end">
|
||||
<Button type="submit" disabled={isSaving || !name}>
|
||||
{isSaving ? `${t("Creating")}…` : t("Create")}
|
||||
</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default APITokenNew;
|
||||
export default APIKeyNew;
|
||||
@@ -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 (
|
||||
<Scene
|
||||
title={t("API Tokens")}
|
||||
title={t("API Keys")}
|
||||
icon={<CodeIcon />}
|
||||
actions={
|
||||
<>
|
||||
@@ -34,19 +33,20 @@ function ApiKeys() {
|
||||
<Action>
|
||||
<Button
|
||||
type="submit"
|
||||
value={`${t("New token")}…`}
|
||||
onClick={handleNewModalOpen}
|
||||
value={`${t("New API key")}…`}
|
||||
action={createApiKey}
|
||||
context={context}
|
||||
/>
|
||||
</Action>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Heading>{t("API Tokens")}</Heading>
|
||||
<Heading>{t("API Keys")}</Heading>
|
||||
<Text as="p" type="secondary">
|
||||
<Trans
|
||||
defaults="You can create an unlimited amount of personal tokens to authenticate
|
||||
with the API. Tokens have the same permissions as your user account.
|
||||
defaults="Create personal API keys to authenticate with the API and programatically control
|
||||
your workspace's data. API keys have the same permissions as your user account.
|
||||
For more details see the <em>developer documentation</em>."
|
||||
components={{
|
||||
em: (
|
||||
@@ -67,13 +67,6 @@ function ApiKeys() {
|
||||
<ApiKeyListItem key={apiKey.id} apiKey={apiKey} />
|
||||
)}
|
||||
/>
|
||||
<Modal
|
||||
title={t("Create a token")}
|
||||
onRequestClose={handleNewModalClose}
|
||||
isOpen={newModalOpen}
|
||||
>
|
||||
<APITokenNew onSubmit={handleNewModalClose} />
|
||||
</Modal>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <em>developer documentation</em>.": "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 <em>developer documentation</em>.",
|
||||
"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 <em>developer documentation</em>.": "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 <em>developer documentation</em>.",
|
||||
"Active": "Active",
|
||||
"Create a token": "Create a token",
|
||||
"API token copied to clipboard": "API token copied to clipboard",
|
||||
"Copied": "Copied",
|
||||
"Revoking": "Revoking",
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user