feat: Admin V2 API keys (#6883)

Add API key management UI for V2

This PR only adds publishable API key UI. Secret API key management will come in a follow-up PR.
This commit is contained in:
Oli Juhl
2024-04-05 11:55:59 +02:00
committed by GitHub
parent e915169e11
commit eadc5e8a79
36 changed files with 265 additions and 179 deletions

View File

@@ -0,0 +1,7 @@
---
"@medusajs/medusa": patch
"medusa-react": patch
"@medusajs/admin": patch
---
feat: Admin V2 API keys

View File

@@ -803,9 +803,10 @@
"deleteSalesChannelWarning": "You are about to delete the sales channel {{name}}. This action cannot be undone."
},
"apiKeyManagement": {
"domain": "API Key Management",
"domainTitle": "API Keys",
"domain": "{{ keyType }} API Keys",
"createKey": "Create key",
"createPublishableApiKey": "Create Publishable API Key",
"createPublishableApiKey": "Create {{ keyType }} API Key",
"editKey": "Edit key",
"revoke": "Revoke",
"publishableApiKeyHint": "Publishable API keys are used to limit the scope of requests to specific sales channels.",

View File

@@ -64,7 +64,7 @@ const useDeveloperRoutes = (): NavItemProps[] => {
return useMemo(
() => [
{
label: t("apiKeyManagement.domain"),
label: t("apiKeyManagement.domainTitle"),
to: "/settings/api-key-management",
},
{

View File

@@ -111,7 +111,7 @@ const useLinks = (): CommandGroupProps[] => {
label: t("salesChannels.domain"),
},
{
label: t("apiKeyManagement.domain"),
label: t("apiKeyManagement.domainTitle"),
},
],
},

View File

@@ -0,0 +1,4 @@
export function upperCaseFirst(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1)
}

View File

@@ -768,58 +768,6 @@ export const v1Routes: RouteObject[] = [
},
],
},
{
path: "api-key-management",
element: <Outlet />,
handle: {
crumb: () => "API Key Management",
},
children: [
{
path: "",
lazy: () =>
import(
"../../routes/api-key-management/api-key-management-list"
),
children: [
{
path: "create",
lazy: () =>
import(
"../../routes/api-key-management/api-key-management-create"
),
},
],
},
{
path: ":id",
lazy: () =>
import(
"../../routes/api-key-management/api-key-management-detail"
),
handle: {
crumb: (data: AdminPublishableApiKeysRes) =>
data.publishable_api_key.title,
},
children: [
{
path: "edit",
lazy: () =>
import(
"../../routes/api-key-management/api-key-management-edit"
),
},
{
path: "add-sales-channels",
lazy: () =>
import(
"../../routes/api-key-management/api-key-management-add-sales-channels"
),
},
],
},
],
},
...settingsExtensions,
],
},

View File

@@ -8,6 +8,7 @@ import { Outlet } from "react-router-dom"
import { SearchProvider } from "../search-provider"
import { SettingsLayout } from "../../components/layout/settings-layout"
import { SidebarProvider } from "../sidebar-provider"
import { ApiKeyDTO } from "@medusajs/types"
import { Spinner } from "@medusajs/icons"
import { useV2Session } from "../../lib/api-v2"
@@ -332,6 +333,57 @@ export const v2Routes: RouteObject[] = [
},
],
},
{
path: "api-key-management",
element: <Outlet />,
handle: {
crumb: () => "API Key Management",
},
children: [
{
path: "",
lazy: () =>
import(
"../../v2-routes/api-key-management/api-key-management-list"
),
children: [
{
path: "create",
lazy: () =>
import(
"../../v2-routes/api-key-management/api-key-management-create"
),
},
],
},
{
path: ":id",
lazy: () =>
import(
"../../v2-routes/api-key-management/api-key-management-detail"
),
handle: {
crumb: (data: { api_key: ApiKeyDTO }) => data.api_key.title,
},
children: [
{
path: "edit",
lazy: () =>
import(
"../../v2-routes/api-key-management/api-key-management-edit"
),
},
{
path: "add-sales-channels",
lazy: () =>
import(
"../../v2-routes/api-key-management/api-key-management-add-sales-channels"
),
},
],
},
],
},
],
},
],

View File

@@ -1,26 +0,0 @@
import { useAdminPublishableApiKeySalesChannels } from "medusa-react"
import { useParams } from "react-router-dom"
import { RouteFocusModal } from "../../../components/route-modal"
import { AddSalesChannelsToApiKeyForm } from "./components"
export const ApiKeyManagementAddSalesChannels = () => {
const { id } = useParams()
const { sales_channels, isLoading, isError, error } =
useAdminPublishableApiKeySalesChannels(id!)
if (isError) {
throw error
}
return (
<RouteFocusModal>
{!isLoading && sales_channels && (
<AddSalesChannelsToApiKeyForm
apiKey={id!}
preSelected={sales_channels.map((sc) => sc.id)}
/>
)}
</RouteFocusModal>
)
}

View File

@@ -0,0 +1,36 @@
import { adminPublishableApiKeysKeys, useAdminCustomQuery } from "medusa-react"
import { useParams } from "react-router-dom"
import { RouteFocusModal } from "../../../components/route-modal"
import { AddSalesChannelsToApiKeyForm } from "./components"
export const ApiKeyManagementAddSalesChannels = () => {
const { id } = useParams()
const { data, isLoading, isError, error } = useAdminCustomQuery(
`/api-keys/${id}`,
[adminPublishableApiKeysKeys.detailSalesChannels(id!)],
{
fields: "id,*sales_channels",
},
{
keepPreviousData: true,
}
)
if (isError) {
throw error
}
const salesChannels = data?.api_key?.sales_channels
return (
<RouteFocusModal>
{!isLoading && salesChannels && (
<AddSalesChannelsToApiKeyForm
apiKey={id!}
preSelected={salesChannels.map((sc) => sc.id)}
/>
)}
</RouteFocusModal>
)
}

View File

@@ -18,7 +18,9 @@ import {
useReactTable,
} from "@tanstack/react-table"
import {
adminPublishableApiKeysKeys,
useAdminAddPublishableKeySalesChannelsBatch,
useAdminCustomPost,
useAdminSalesChannels,
} from "medusa-react"
import { useEffect, useMemo, useState } from "react"
@@ -61,8 +63,10 @@ export const AddSalesChannelsToApiKeyForm = ({
const { setValue } = form
const { mutateAsync, isLoading: isMutating } =
useAdminAddPublishableKeySalesChannelsBatch(apiKey)
const { mutateAsync, isLoading: isMutating } = useAdminCustomPost(
`/api-keys/${apiKey}/sales-channels/batch/add`,
[adminPublishableApiKeysKeys.detailSalesChannels(apiKey)]
)
const [{ pageIndex, pageSize }, setPagination] = useState<PaginationState>({
pageIndex: 0,
@@ -128,7 +132,7 @@ export const AddSalesChannelsToApiKeyForm = ({
const handleSubmit = form.handleSubmit(async (values) => {
await mutateAsync(
{
sales_channel_ids: values.sales_channel_ids.map((p) => ({ id: p })),
sales_channel_ids: values.sales_channel_ids,
},
{
onSuccess: () => {

View File

@@ -1,6 +1,6 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { Button, Heading, Input, Text } from "@medusajs/ui"
import { useAdminCreatePublishableApiKey } from "medusa-react"
import { adminPublishableApiKeysKeys, useAdminCustomPost } from "medusa-react"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import * as zod from "zod"
@@ -26,14 +26,19 @@ export const CreatePublishableApiKeyForm = () => {
resolver: zodResolver(CreatePublishableApiKeySchema),
})
const { mutateAsync, isLoading } = useAdminCreatePublishableApiKey()
const { mutateAsync, isLoading } = useAdminCustomPost(`/admin/api-keys`, [
adminPublishableApiKeysKeys.lists(),
])
const handleSubmit = form.handleSubmit(async (values) => {
await mutateAsync(values, {
onSuccess: ({ publishable_api_key }) => {
handleSuccess(`/settings/api-key-management/${publishable_api_key.id}`)
},
})
await mutateAsync(
{ title: values.title, type: "publishable" },
{
onSuccess: () => {
handleSuccess(`/settings/api-key-management`)
},
}
)
})
return (

View File

@@ -1,4 +1,4 @@
import { useAdminPublishableApiKey } from "medusa-react"
import { adminPublishableApiKeysKeys, useAdminCustomQuery } from "medusa-react"
import { Outlet, json, useLoaderData, useParams } from "react-router-dom"
import { JsonViewSection } from "../../../components/common/json-view-section"
import { ApiKeyGeneralSection } from "./components/api-key-general-section"
@@ -11,16 +11,20 @@ export const ApiKeyManagementDetail = () => {
>
const { id } = useParams()
const { publishable_api_key, isLoading, isError, error } =
useAdminPublishableApiKey(id!, {
initialData,
})
const { data, isLoading, isError, error } = useAdminCustomQuery(
`/api-keys/${id}`,
[adminPublishableApiKeysKeys.detail(id!)],
undefined,
{
initialData: initialData,
}
)
if (isLoading) {
return <div>Loading...</div>
}
if (isError || !publishable_api_key) {
if (isError || !data?.api_key) {
if (error) {
throw error
}
@@ -30,9 +34,9 @@ export const ApiKeyManagementDetail = () => {
return (
<div className="flex flex-col gap-y-2">
<ApiKeyGeneralSection apiKey={publishable_api_key} />
<ApiKeySalesChannelSection apiKey={publishable_api_key} />
<JsonViewSection data={publishable_api_key} />
<ApiKeyGeneralSection apiKey={data?.api_key} />
<ApiKeySalesChannelSection apiKey={data?.api_key} />
<JsonViewSection data={data?.api_key} />
<Outlet />
</div>
)

View File

@@ -9,6 +9,7 @@ import {
usePrompt,
} from "@medusajs/ui"
import {
useAdminCustomQuery,
useAdminDeletePublishableApiKey,
useAdminRevokePublishableApiKey,
useAdminUser,
@@ -18,9 +19,10 @@ import { useNavigate } from "react-router-dom"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { Skeleton } from "../../../../../components/common/skeleton"
import { UserLink } from "../../../../../components/common/user-link"
import { ApiKeyDTO } from "@medusajs/types"
type ApiKeyGeneralSectionProps = {
apiKey: PublishableApiKey
apiKey: ApiKeyDTO
}
export const ApiKeyGeneralSection = ({ apiKey }: ApiKeyGeneralSectionProps) => {
@@ -121,10 +123,10 @@ export const ApiKeyGeneralSection = ({ apiKey }: ApiKeyGeneralSectionProps) => {
</Text>
<div className="bg-ui-bg-subtle border-ui-border-base box-border flex w-fit cursor-default items-center gap-x-0.5 overflow-hidden rounded-full border pl-2 pr-1">
<Text size="xsmall" leading="compact" className="truncate">
{apiKey.id}
{apiKey.redacted}
</Text>
<Copy
content={apiKey.id}
content={apiKey.token}
variant="mini"
className="text-ui-fg-subtle"
/>
@@ -149,9 +151,12 @@ export const ApiKeyGeneralSection = ({ apiKey }: ApiKeyGeneralSectionProps) => {
}
const ActionBy = ({ userId }: { userId: string | null }) => {
const { user, isLoading, isError, error } = useAdminUser(userId!, {
enabled: !!userId,
})
const { data, isLoading, isError, error } = useAdminCustomQuery(
`/users/${userId}`,
[],
{},
{ enabled: !!userId }
)
if (!userId) {
return (
@@ -174,7 +179,7 @@ const ActionBy = ({ userId }: { userId: string | null }) => {
)
}
if (!user) {
if (!data?.user) {
return (
<Text size="small" className="text-ui-fg-subtle">
-
@@ -182,5 +187,5 @@ const ActionBy = ({ userId }: { userId: string | null }) => {
)
}
return <UserLink {...user} />
return <UserLink {...data.user} />
}

View File

@@ -20,7 +20,9 @@ import {
useReactTable,
} from "@tanstack/react-table"
import {
useAdminPublishableApiKeySalesChannels,
adminPublishableApiKeysKeys,
useAdminCustomPost,
useAdminCustomQuery,
useAdminRemovePublishableKeySalesChannelsBatch,
} from "medusa-react"
import { useMemo, useState } from "react"
@@ -34,9 +36,10 @@ import {
import { Query } from "../../../../../components/filtering/query"
import { LocalizedTablePagination } from "../../../../../components/localization/localized-table-pagination"
import { useQueryParams } from "../../../../../hooks/use-query-params"
import { ApiKeyDTO } from "@medusajs/types"
type ApiKeySalesChannelSectionProps = {
apiKey: PublishableApiKey
apiKey: ApiKeyDTO
}
const PAGE_SIZE = 10
@@ -64,23 +67,28 @@ export const ApiKeySalesChannelSection = ({
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
const params = useQueryParams(["q"])
const { sales_channels, isLoading, isError, error } =
useAdminPublishableApiKeySalesChannels(
apiKey.id,
{
...params,
},
{
keepPreviousData: true,
}
)
const count = sales_channels?.length || 0
const query = {
...params,
fields: "id,*sales_channels",
}
const { data, isLoading, isError, error } = useAdminCustomQuery(
`/api-keys/${apiKey.id}`,
[adminPublishableApiKeysKeys.detailSalesChannels(apiKey.id, query)],
query,
{
keepPreviousData: true,
}
)
const salesChannels = data?.api_key?.sales_channels
const count = salesChannels?.length || 0
const columns = useColumns()
const table = useReactTable({
data: sales_channels ?? [],
data: salesChannels ?? [],
columns,
pageCount: Math.ceil(count / PAGE_SIZE),
state: {
@@ -97,8 +105,9 @@ export const ApiKeySalesChannelSection = ({
},
})
const { mutateAsync } = useAdminRemovePublishableKeySalesChannelsBatch(
apiKey.id
const { mutateAsync } = useAdminCustomPost(
`/api-keys/${apiKey.id}/sales-channels/batch/remove`,
[adminPublishableApiKeysKeys.detailSalesChannels(apiKey.id)]
)
const handleRemove = async () => {
@@ -119,7 +128,7 @@ export const ApiKeySalesChannelSection = ({
await mutateAsync(
{
sales_channel_ids: keys.map((k) => ({ id: k })),
sales_channel_ids: keys,
},
{
onSuccess: () => {
@@ -129,7 +138,7 @@ export const ApiKeySalesChannelSection = ({
)
}
const noRecords = !isLoading && !sales_channels?.length && !params.q
const noRecords = !isLoading && !salesChannels?.length && !params.q
if (isError) {
throw error
@@ -155,7 +164,7 @@ export const ApiKeySalesChannelSection = ({
<NoRecords />
) : (
<div>
{!isLoading && sales_channels?.length !== 0 ? (
{!isLoading && salesChannels?.length !== 0 ? (
<Table>
<Table.Header className="border-t-0">
{table.getHeaderGroups().map((headerGroup) => {

View File

@@ -1,13 +1,13 @@
import { AdminPublishableApiKeysRes } from "@medusajs/medusa"
import { Response } from "@medusajs/medusa-js"
import { adminProductKeys } from "medusa-react"
import { adminPublishableApiKeysKeys } from "medusa-react"
import { LoaderFunctionArgs } from "react-router-dom"
import { ApiKeyDTO } from "@medusajs/types"
import { medusa, queryClient } from "../../../lib/medusa"
const apiKeyDetailQuery = (id: string) => ({
queryKey: adminProductKeys.detail(id),
queryFn: async () => medusa.admin.publishableApiKeys.retrieve(id),
queryKey: adminPublishableApiKeysKeys.detail(id),
queryFn: async () => medusa.admin.custom.get(`/api-keys/${id}`),
})
export const apiKeyLoader = async ({ params }: LoaderFunctionArgs) => {
@@ -15,7 +15,7 @@ export const apiKeyLoader = async ({ params }: LoaderFunctionArgs) => {
const query = apiKeyDetailQuery(id!)
return (
queryClient.getQueryData<Response<AdminPublishableApiKeysRes>>(
queryClient.getQueryData<Response<{ api_key: ApiKeyDTO }>>(
query.queryKey
) ?? (await queryClient.fetchQuery(query))
)

View File

@@ -1,7 +1,7 @@
import { Heading } from "@medusajs/ui"
import { useAdminPublishableApiKey } from "medusa-react"
import { adminPublishableApiKeysKeys, useAdminCustomQuery } from "medusa-react"
import { useTranslation } from "react-i18next"
import { useParams } from "react-router-dom"
import { json, useParams } from "react-router-dom"
import { RouteDrawer } from "../../../components/route-modal"
import { EditApiKeyForm } from "./components/edit-api-key-form"
@@ -9,11 +9,17 @@ export const ApiKeyManagementEdit = () => {
const { id } = useParams()
const { t } = useTranslation()
const { publishable_api_key, isLoading, isError, error } =
useAdminPublishableApiKey(id!)
const { data, isLoading, isError, error } = useAdminCustomQuery(
`/api-keys/${id}`,
[adminPublishableApiKeysKeys.detail(id!)]
)
if (isError) {
throw error
if (isError || !data?.api_key) {
if (error) {
throw error
}
throw json("An unknown error has occured", 500)
}
return (
@@ -21,8 +27,8 @@ export const ApiKeyManagementEdit = () => {
<RouteDrawer.Header>
<Heading>{t("apiKeyManagement.editKey")}</Heading>
</RouteDrawer.Header>
{!isLoading && publishable_api_key && (
<EditApiKeyForm apiKey={publishable_api_key} />
{!isLoading && !!data?.api_key && (
<EditApiKeyForm apiKey={data.api_key} />
)}
</RouteDrawer>
)

View File

@@ -1,7 +1,6 @@
import { zodResolver } from "@hookform/resolvers/zod"
import type { PublishableApiKey } from "@medusajs/medusa"
import { Button, Input } from "@medusajs/ui"
import { useAdminUpdatePublishableApiKey } from "medusa-react"
import { adminPublishableApiKeysKeys, useAdminCustomPost } from "medusa-react"
import { useForm } from "react-hook-form"
import { useTranslation } from "react-i18next"
import * as zod from "zod"
@@ -11,9 +10,10 @@ import {
RouteDrawer,
useRouteModal,
} from "../../../../../components/route-modal"
import { ApiKeyDTO } from "@medusajs/types"
type EditApiKeyFormProps = {
apiKey: PublishableApiKey
apiKey: ApiKeyDTO
}
const EditApiKeySchema = zod.object({
@@ -31,7 +31,14 @@ export const EditApiKeyForm = ({ apiKey }: EditApiKeyFormProps) => {
resolver: zodResolver(EditApiKeySchema),
})
const { mutateAsync, isLoading } = useAdminUpdatePublishableApiKey(apiKey.id)
const { mutateAsync, isLoading } = useAdminCustomPost(
`/api-keys/${apiKey.id}`,
[
adminPublishableApiKeysKeys.lists(),
adminPublishableApiKeysKeys.detail(apiKey.id),
adminPublishableApiKeysKeys.details(),
]
)
const handleSubmit = form.handleSubmit(async (data) => {
await mutateAsync(data, {

View File

@@ -1,10 +1,13 @@
import { Outlet } from "react-router-dom"
import { ApiKeyManagementListTable } from "./components/api-key-management-list-table"
// TODO: Add secret API keys
export const ApiKeyManagementList = () => {
return (
<div className="flex flex-col gap-y-2">
<ApiKeyManagementListTable />
<ApiKeyManagementListTable keyType="publishable" />
{/* <ApiKeyManagementListTable keyType="secret" /> */}
<Outlet />
</div>
)

View File

@@ -1,5 +1,5 @@
import { Button, Container, Heading } from "@medusajs/ui"
import { useAdminPublishableApiKeys } from "medusa-react"
import { adminPublishableApiKeysKeys, useAdminCustomQuery } from "medusa-react"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { DataTable } from "../../../../../components/table/data-table"
@@ -7,30 +7,40 @@ import { useDataTable } from "../../../../../hooks/use-data-table"
import { useApiKeyManagementTableColumns } from "./use-api-key-management-table-columns"
import { useApiKeyManagementTableFilters } from "./use-api-key-management-table-filters"
import { useApiKeyManagementTableQuery } from "./use-api-key-management-table-query"
import { upperCaseFirst } from "../../../../../lib/uppercase-first"
const PAGE_SIZE = 20
export const ApiKeyManagementListTable = () => {
export const ApiKeyManagementListTable = ({
keyType,
}: {
keyType: "secret" | "publishable"
}) => {
const { t } = useTranslation()
const { searchParams, raw } = useApiKeyManagementTableQuery({
pageSize: PAGE_SIZE,
})
const { publishable_api_keys, count, isLoading, isError, error } =
useAdminPublishableApiKeys(
{
...searchParams,
},
{
keepPreviousData: true,
}
)
const query = {
...searchParams,
type: keyType,
fields:
"id,title,redacted,token,type,created_at,updated_at,revoked_at,last_used_at,created_by,revoked_by",
}
// @ts-ignore
const { data, count, isLoading, isError, error } = useAdminCustomQuery(
"/api-keys",
[adminPublishableApiKeysKeys.list(query)],
query
)
const filters = useApiKeyManagementTableFilters()
const columns = useApiKeyManagementTableColumns()
const { table } = useDataTable({
data: publishable_api_keys || [],
data: data?.api_keys || [],
columns,
count,
enablePagination: true,
@@ -49,7 +59,9 @@ export const ApiKeyManagementListTable = () => {
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">{t("apiKeyManagement.domain")}</Heading>
<Heading level="h2">
{t(`apiKeyManagement.domain`, { keyType: upperCaseFirst(keyType) })}
</Heading>
<Link to="create">
<Button variant="secondary" size="small">
{t("actions.create")}

View File

@@ -3,6 +3,9 @@ import { PublishableApiKey } from "@medusajs/medusa"
import { Copy, Text, usePrompt } from "@medusajs/ui"
import { createColumnHelper } from "@tanstack/react-table"
import {
adminPublishableApiKeysKeys,
useAdminCustomDelete,
useAdminCustomPost,
useAdminDeletePublishableApiKey,
useAdminRevokePublishableApiKey,
} from "medusa-react"
@@ -12,8 +15,9 @@ import { useTranslation } from "react-i18next"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DateCell } from "../../../../../components/table/table-cells/common/date-cell"
import { StatusCell } from "../../../../../components/table/table-cells/common/status-cell"
import { ApiKeyDTO } from "@medusajs/types"
const columnHelper = createColumnHelper<PublishableApiKey>()
const columnHelper = createColumnHelper<ApiKeyDTO>()
export const useApiKeyManagementTableColumns = () => {
const { t } = useTranslation()
@@ -28,9 +32,9 @@ export const useApiKeyManagementTableColumns = () => {
</div>
),
}),
columnHelper.accessor("id", {
header: "Key",
cell: ({ getValue }) => {
columnHelper.accessor("redacted", {
header: "Token",
cell: ({ getValue, row }) => {
const token = getValue()
return (
@@ -42,7 +46,7 @@ export const useApiKeyManagementTableColumns = () => {
{token}
</Text>
<Copy
content={token}
content={row.original.token}
variant="mini"
className="text-ui-fg-subtle"
/>
@@ -73,7 +77,7 @@ export const useApiKeyManagementTableColumns = () => {
columnHelper.display({
id: "actions",
cell: ({ row }) => {
return <ApiKeyActions apiKey={row.original} />
return <ApiKeyActions apiKey={row.original as any} />
},
}),
],
@@ -82,11 +86,19 @@ export const useApiKeyManagementTableColumns = () => {
}
const ApiKeyActions = ({ apiKey }: { apiKey: PublishableApiKey }) => {
const { mutateAsync: revokeAsync } = useAdminRevokePublishableApiKey(
apiKey.id
const { mutateAsync: revokeAsync } = useAdminCustomPost(
`/api-keys/${apiKey.id}/revoke`,
[
adminPublishableApiKeysKeys.lists(),
adminPublishableApiKeysKeys.detail(apiKey.id),
]
)
const { mutateAsync: deleteAsync } = useAdminDeletePublishableApiKey(
apiKey.id
const { mutateAsync: deleteAsync } = useAdminCustomDelete(
`/api-keys/${apiKey.id}`,
[
adminPublishableApiKeysKeys.lists(),
adminPublishableApiKeysKeys.detail(apiKey.id),
]
)
const { t } = useTranslation()
@@ -123,7 +135,7 @@ const ApiKeyActions = ({ apiKey }: { apiKey: PublishableApiKey }) => {
return
}
await revokeAsync()
await revokeAsync({})
}
return (

View File

@@ -7,14 +7,10 @@ export const useApiKeyManagementTableFilters = () => {
let filters: Filter[] = []
const dateFilters: Filter[] = [
{ label: t("fields.createdAt"), key: "created_at" },
{ label: t("fields.updatedAt"), key: "updated_at" },
{ label: t("fields.revokedAt"), key: "revoked_at" },
].map((f) => ({
key: f.key,
label: f.label,
type: "date",
}))
{ label: t("fields.createdAt"), key: "created_at", type: "date" },
{ label: t("fields.updatedAt"), key: "updated_at", type: "date" },
{ label: t("fields.revokedAt"), key: "revoked_at", type: "date" },
]
filters = [...filters, ...dateFilters]

View File

@@ -1,3 +1,4 @@
export * from "./admin"
export * from "./store"
export * from "./utils"

View File

@@ -30,7 +30,7 @@ export const GET = async (
const [apiKey] = await remoteQuery(queryObject)
res.status(200).json({ apiKey })
res.status(200).json({ api_key: apiKey })
}
export const POST = async (