Openstatus
www.openstatus.dev
1import { z } from "zod";
2
3import { Events } from "@openstatus/analytics";
4import { db, eq } from "@openstatus/db";
5import { user, usersToWorkspaces, workspace } from "@openstatus/db/src/schema";
6import { createApiKeySchema } from "@openstatus/db/src/schema/api-keys/validation";
7
8import { TRPCError } from "@trpc/server";
9import {
10 createApiKey as createCustomApiKey,
11 getApiKeys,
12 revokeApiKey,
13} from "../service/apiKey";
14import { createTRPCRouter, protectedProcedure } from "../trpc";
15
16export const apiKeyRouter = createTRPCRouter({
17 create: protectedProcedure
18 .meta({ track: Events.CreateAPI })
19 .input(createApiKeySchema)
20 .mutation(async ({ input, ctx }) => {
21 // Verify user has access to the workspace
22 const allowedWorkspaces = await db
23 .select()
24 .from(usersToWorkspaces)
25 .innerJoin(user, eq(user.id, usersToWorkspaces.userId))
26 .innerJoin(workspace, eq(workspace.id, usersToWorkspaces.workspaceId))
27 .where(eq(user.id, ctx.user.id))
28 .all();
29
30 const allowedIds = allowedWorkspaces.map((i) => i.workspace.id);
31
32 if (!allowedIds.includes(ctx.workspace.id)) {
33 throw new TRPCError({
34 code: "UNAUTHORIZED",
35 message: "Unauthorized",
36 });
37 }
38
39 // Create the API key using the custom service
40 const { token, key } = await createCustomApiKey(
41 ctx.workspace.id,
42 ctx.user.id,
43 input.name,
44 input.description,
45 input.expiresAt,
46 );
47
48 // Return both the key details and the full token (one-time display)
49 return {
50 token,
51 key,
52 };
53 }),
54
55 revoke: protectedProcedure
56 .meta({ track: Events.RevokeAPI })
57 .input(z.object({ keyId: z.number() }))
58 .mutation(async ({ input, ctx }) => {
59 // Revoke the key with workspace ownership verification
60 const success = await revokeApiKey(input.keyId, ctx.workspace.id);
61
62 if (!success) {
63 throw new TRPCError({
64 code: "NOT_FOUND",
65 message: "API key not found or unauthorized",
66 });
67 }
68
69 return;
70 }),
71
72 getAll: protectedProcedure.query(async ({ ctx }) => {
73 // Get all API keys for the workspace
74 const keys = await getApiKeys(ctx.workspace.id);
75
76 // Fetch user information for each key's creator
77 const keysWithUserInfo = await Promise.all(
78 keys.map(async (key) => {
79 const creator = await db
80 .select({
81 id: user.id,
82 email: user.email,
83 firstName: user.firstName,
84 lastName: user.lastName,
85 })
86 .from(user)
87 .where(eq(user.id, key.createdById))
88 .get();
89
90 return {
91 ...key,
92 createdBy: creator,
93 };
94 }),
95 );
96
97 return keysWithUserInfo;
98 }),
99});