Skip to content

Commit 52bca58

Browse files
committed
Merge branch 'main' of https://github.com/e2b-dev/dashboard into refactor/webhooks-page
2 parents 92c521a + fa38e5e commit 52bca58

32 files changed

Lines changed: 1143 additions & 657 deletions

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
6868
# NEXT_PUBLIC_INCLUDE_REPORT_ISSUE=0
6969

7070
### Enable dashboard status indicator feature: set to 1 to enable
71-
### When enabled, the E2B status is read from https://status.e2b.dev
71+
### When enabled, the E2B status is read from the incident.io summary API
7272
# NEXT_PUBLIC_INCLUDE_STATUS_INDICATOR=0
7373

7474
### Set to 1 to use mock data

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ CLAUDE.md
4545
.cursor/
4646
.agent
4747

48+
# Cursor IDE
49+
.cursor/
4850

4951
# tooling
5052
/template
Lines changed: 13 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,23 @@
1-
import CreateApiKeyDialog from '@/features/dashboard/settings/keys/create-api-key-dialog'
2-
import ApiKeysTable from '@/features/dashboard/settings/keys/table'
3-
import Frame from '@/ui/frame'
4-
import { Button } from '@/ui/primitives/button'
5-
import {
6-
Card,
7-
CardContent,
8-
CardDescription,
9-
CardHeader,
10-
CardTitle,
11-
} from '@/ui/primitives/card'
12-
import { AddIcon } from '@/ui/primitives/icons'
1+
import { Page } from '@/features/dashboard/layouts/page'
2+
import { ApiKeysPageContent } from '@/features/dashboard/settings/keys'
3+
import { HydrateClient, prefetch, trpc } from '@/trpc/server'
134

14-
interface KeysPageClientProps {
5+
interface KeysPageProps {
156
params: Promise<{
167
teamSlug: string
178
}>
189
}
1910

20-
export default async function KeysPage({ params }: KeysPageClientProps) {
21-
return (
22-
<Frame
23-
classNames={{
24-
wrapper: 'w-full max-md:p-0',
25-
frame: 'max-md:border-none',
26-
}}
27-
>
28-
<Card className="w-full">
29-
<CardHeader>
30-
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between sm:gap-6">
31-
<div className="flex flex-col gap-2">
32-
<CardTitle>Manage API Keys</CardTitle>
33-
<CardDescription className="max-w-[400px]">
34-
API Keys are used to authenticate API requests from your teams
35-
applications.
36-
</CardDescription>
37-
</div>
11+
export default async function KeysPage({ params }: KeysPageProps) {
12+
const { teamSlug } = await params
3813

39-
<CreateApiKeyDialog>
40-
<Button className="w-full sm:w-auto sm:self-start">
41-
<AddIcon className="size-4" /> CREATE KEY
42-
</Button>
43-
</CreateApiKeyDialog>
44-
</div>
45-
</CardHeader>
14+
prefetch(trpc.teams.listApiKeys.queryOptions({ teamSlug }))
4615

47-
<CardContent>
48-
<div className="w-full overflow-x-auto">
49-
<ApiKeysTable params={params} className="min-w-[800px]" />
50-
</div>
51-
</CardContent>
52-
</Card>
53-
</Frame>
16+
return (
17+
<HydrateClient>
18+
<Page>
19+
<ApiKeysPageContent />
20+
</Page>
21+
</HydrateClient>
5422
)
5523
}

src/configs/cache.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
export const CACHE_TAGS = {
22
TEAM_ID_FROM_SLUG: (segment: string) => `team-id-from-slug-${segment}`,
33
TEAM_USAGE: (teamId: string) => `team-usage-${teamId}`,
4-
TEAM_API_KEYS: (teamId: string) => `team-api-keys-${teamId}`,
54

65
DEFAULT_TEMPLATES: 'default-templates',
76
} as const

src/core/modules/keys/schemas.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { z } from 'zod'
2+
3+
const API_KEY_NAME_MAX_LENGTH = 50
4+
5+
const CreateApiKeySchema = z.object({
6+
name: z
7+
.string({ error: 'Name is required' })
8+
.min(1, 'Name cannot be empty')
9+
.max(API_KEY_NAME_MAX_LENGTH, {
10+
message: `Name cannot be longer than ${API_KEY_NAME_MAX_LENGTH} characters`,
11+
})
12+
.trim(),
13+
})
14+
15+
export { CreateApiKeySchema }

src/core/server/actions/key-actions.ts

Lines changed: 0 additions & 100 deletions
This file was deleted.

src/core/server/api/routers/teams.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { fileTypeFromBuffer } from 'file-type'
33
import { revalidatePath } from 'next/cache'
44
import { after } from 'next/server'
55
import { z } from 'zod'
6+
import { createKeysRepository } from '@/core/modules/keys/repository.server'
7+
import { CreateApiKeySchema } from '@/core/modules/keys/schemas'
68
import {
79
AddTeamMemberSchema,
810
CreateTeamSchema,
@@ -42,6 +44,12 @@ const teamsRepositoryProcedure = protectedTeamProcedure.use(
4244
}))
4345
)
4446

47+
const keysRepositoryProcedure = protectedTeamProcedure.use(
48+
withTeamAuthedRequestRepository(createKeysRepository, (keysRepository) => ({
49+
keysRepository,
50+
}))
51+
)
52+
4553
const getStorageFilePath = (folderPath: string, fileName: string) =>
4654
`${folderPath}/${fileName}`
4755

@@ -55,6 +63,67 @@ export const teamsRouter = createTRPCRouter({
5563

5664
return teamsResult.data
5765
}),
66+
67+
listApiKeys: keysRepositoryProcedure.query(async ({ ctx }) => {
68+
const result = await ctx.keysRepository.listTeamApiKeys()
69+
70+
if (!result.ok) {
71+
throwTRPCErrorFromRepoError(result.error)
72+
}
73+
74+
return { apiKeys: result.data }
75+
}),
76+
77+
createApiKey: keysRepositoryProcedure
78+
.input(CreateApiKeySchema)
79+
.mutation(async ({ ctx, input }) => {
80+
const { name } = input
81+
82+
const result = await ctx.keysRepository.createApiKey(name)
83+
84+
if (!result.ok) {
85+
l.error({
86+
key: 'create_api_key_trpc:error',
87+
message: result.error.message,
88+
error: result.error,
89+
team_id: ctx.teamId,
90+
user_id: ctx.session.user.id,
91+
context: { name },
92+
})
93+
94+
throwTRPCErrorFromRepoError(result.error)
95+
}
96+
97+
return { createdApiKey: result.data }
98+
}),
99+
100+
deleteApiKey: keysRepositoryProcedure
101+
.input(
102+
z.object({
103+
apiKeyId: z.uuid(),
104+
})
105+
)
106+
.mutation(async ({ ctx, input }) => {
107+
const { apiKeyId } = input
108+
109+
const result = await ctx.keysRepository.deleteApiKey(apiKeyId)
110+
111+
if (!result.ok) {
112+
l.error({
113+
key: 'delete_api_key_trpc:error',
114+
message: result.error.message,
115+
error: result.error,
116+
team_id: ctx.teamId,
117+
user_id: ctx.session.user.id,
118+
context: { apiKeyId },
119+
})
120+
121+
throwTRPCErrorFromRepoError(result.error)
122+
}
123+
124+
return undefined
125+
}),
126+
58127
create: userTeamsRepositoryProcedure
59128
.input(CreateTeamSchema)
60129
.mutation(async ({ ctx, input }) => {

src/core/server/functions/keys/get-api-keys.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

src/features/dashboard/account/user-access-token.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import CopyButton from '@/ui/copy-button'
88
import { IconButton } from '@/ui/primitives/icon-button'
99
import { EyeIcon, EyeOffIcon } from '@/ui/primitives/icons'
1010
import { Input } from '@/ui/primitives/input'
11-
import { Loader } from '@/ui/primitives/loader_d'
11+
import { Loader } from '@/ui/primitives/loader'
1212

1313
interface UserAccessTokenProps {
1414
className?: string
@@ -57,7 +57,7 @@ export default function UserAccessToken({ className }: UserAccessTokenProps) {
5757
disabled={isPending}
5858
>
5959
{isPending ? (
60-
<Loader />
60+
<Loader variant="square" size="lg" />
6161
) : token ? (
6262
isVisible ? (
6363
<EyeOffIcon />

src/features/dashboard/billing/invoices.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,14 @@ function InvoicesEmpty({ error }: InvoicesEmptyProps) {
4949
return (
5050
<TableEmptyState colSpan={4}>
5151
<InvoiceIcon
52-
className={cn('size-4', error && 'text-accent-error-highlight')}
53-
/>
54-
<p
5552
className={cn(
56-
'prose-body-highlight',
53+
'size-4 shrink-0',
5754
error && 'text-accent-error-highlight'
5855
)}
59-
>
56+
/>
57+
<span className={cn(error && 'text-accent-error-highlight')}>
6058
{error ? error : 'No invoices yet'}
61-
</p>
59+
</span>
6260
</TableEmptyState>
6361
)
6462
}

0 commit comments

Comments
 (0)