feat: api token page in the settings
This commit is contained in:
21
apps/web/src/app/(dashboard)/settings/token/page.tsx
Normal file
21
apps/web/src/app/(dashboard)/settings/token/page.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||||
|
|
||||||
|
import { ApiTokenForm } from '~/components/forms/token';
|
||||||
|
|
||||||
|
export default async function ApiToken() {
|
||||||
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-medium">API Token</h3>
|
||||||
|
|
||||||
|
<p className="text-muted-foreground mt-2 text-sm">
|
||||||
|
On this page, you can create new API tokens and manage the existing ones.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<hr className="my-4" />
|
||||||
|
|
||||||
|
<ApiTokenForm user={user} className="max-w-xl" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { HTMLAttributes } from 'react';
|
import type { HTMLAttributes } from 'react';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
|
|
||||||
import { CreditCard, Key, User } from 'lucide-react';
|
import { Braces, CreditCard, Key, User } from 'lucide-react';
|
||||||
|
|
||||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
@@ -48,6 +48,19 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<Link href="/settings/token">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className={cn(
|
||||||
|
'w-full justify-start',
|
||||||
|
pathname?.startsWith('/settings/token') && 'bg-secondary',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Braces className="mr-2 h-5 w-5" />
|
||||||
|
API Token
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
|
||||||
{isBillingEnabled && (
|
{isBillingEnabled && (
|
||||||
<Link href="/settings/billing">
|
<Link href="/settings/billing">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { HTMLAttributes } from 'react';
|
import type { HTMLAttributes } from 'react';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
|
|
||||||
import { CreditCard, Key, User } from 'lucide-react';
|
import { Braces, CreditCard, Key, User } from 'lucide-react';
|
||||||
|
|
||||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
@@ -51,6 +51,19 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<Link href="/settings/token">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className={cn(
|
||||||
|
'w-full justify-start',
|
||||||
|
pathname?.startsWith('/settings/token') && 'bg-secondary',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Braces className="mr-2 h-5 w-5" />
|
||||||
|
API Token
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
|
||||||
{isBillingEnabled && (
|
{isBillingEnabled && (
|
||||||
<Link href="/settings/billing">
|
<Link href="/settings/billing">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
65
packages/trpc/server/api-token-router/router.ts
Normal file
65
packages/trpc/server/api-token-router/router.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { TRPCError } from '@trpc/server';
|
||||||
|
|
||||||
|
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
|
||||||
|
import { deleteApiTokenById } from '@documenso/lib/server-only/public-api/delete-api-token-by-id';
|
||||||
|
import { getApiTokenById } from '@documenso/lib/server-only/public-api/get-api-token-by-id';
|
||||||
|
|
||||||
|
import { authenticatedProcedure, router } from '../trpc';
|
||||||
|
import {
|
||||||
|
ZCreateTokenMutationSchema,
|
||||||
|
ZDeleteTokenByIdMutationSchema,
|
||||||
|
ZGetApiTokenByIdQuerySchema,
|
||||||
|
} from './schema';
|
||||||
|
|
||||||
|
export const apiTokenRouter = router({
|
||||||
|
getTokenById: authenticatedProcedure
|
||||||
|
.input(ZGetApiTokenByIdQuerySchema)
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
try {
|
||||||
|
const { id } = input;
|
||||||
|
|
||||||
|
return await getApiTokenById({
|
||||||
|
id,
|
||||||
|
userId: ctx.user.id,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message: 'We were unable to find this API token. Please try again.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
createToken: authenticatedProcedure
|
||||||
|
.input(ZCreateTokenMutationSchema)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
try {
|
||||||
|
const { tokenName } = input;
|
||||||
|
return await createApiToken({
|
||||||
|
userId: ctx.user.id,
|
||||||
|
tokenName,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message: 'We were unable to create an API token. Please try again.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
deleteTokenById: authenticatedProcedure
|
||||||
|
.input(ZDeleteTokenByIdMutationSchema)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
try {
|
||||||
|
const { id } = input;
|
||||||
|
|
||||||
|
return await deleteApiTokenById({
|
||||||
|
id,
|
||||||
|
userId: ctx.user.id,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message: 'We were unable to delete this API Token. Please try again.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
13
packages/trpc/server/api-token-router/schema.ts
Normal file
13
packages/trpc/server/api-token-router/schema.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const ZGetApiTokenByIdQuerySchema = z.object({
|
||||||
|
id: z.number().min(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ZCreateTokenMutationSchema = z.object({
|
||||||
|
tokenName: z.string().min(3, { message: 'The token name should be 3 characters or longer' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ZDeleteTokenByIdMutationSchema = z.object({
|
||||||
|
id: z.number().min(1),
|
||||||
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { adminRouter } from './admin-router/router';
|
import { adminRouter } from './admin-router/router';
|
||||||
|
import { apiTokenRouter } from './api-token-router/router';
|
||||||
import { authRouter } from './auth-router/router';
|
import { authRouter } from './auth-router/router';
|
||||||
import { documentRouter } from './document-router/router';
|
import { documentRouter } from './document-router/router';
|
||||||
import { fieldRouter } from './field-router/router';
|
import { fieldRouter } from './field-router/router';
|
||||||
@@ -13,6 +14,7 @@ export const appRouter = router({
|
|||||||
field: fieldRouter,
|
field: fieldRouter,
|
||||||
admin: adminRouter,
|
admin: adminRouter,
|
||||||
shareLink: shareLinkRouter,
|
shareLink: shareLinkRouter,
|
||||||
|
apiToken: apiTokenRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AppRouter = typeof appRouter;
|
export type AppRouter = typeof appRouter;
|
||||||
|
|||||||
Reference in New Issue
Block a user