@@ -10,6 +10,7 @@ import { MAX_API_KEY_TASK_IDENTIFIERS } from "~/consts";
1010import { prisma } from "~/db.server" ;
1111import { RuntimeEnvironmentType } from "~/database-types" ;
1212import { canIssueAdditionalApiKeys } from "~/services/additionalApiKeyIssuance.server" ;
13+ import { apiKeyTelemetry , type ApiKeyTelemetry } from "~/services/apiKeyTelemetry.server" ;
1314import { rbac } from "~/services/rbac.server" ;
1415import { generateAdditionalApiKey , generateRootApiKey } from "~/utils/apiKeys" ;
1516import { controlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server" ;
@@ -131,13 +132,15 @@ export async function createEnvironmentApiKey(
131132 prismaClient = prisma ,
132133 rbacController = rbac ,
133134 issuanceAllowed,
135+ telemetryRecorder = apiKeyTelemetry ,
134136 } : {
135137 prismaClient ?: Pick <
136138 PrismaClient ,
137139 "apiKey" | "featureFlag" | "organization" | "runtimeEnvironment" | "taskIdentifier"
138140 > ;
139141 rbacController ?: Pick < HostRbacController , "prepareApiKeyPolicy" > ;
140142 issuanceAllowed ?: ( organizationId : string ) => Promise < boolean > ;
143+ telemetryRecorder ?: ApiKeyTelemetry ;
141144 } = { }
142145) {
143146 const environment = await prismaClient . runtimeEnvironment . findFirst ( {
@@ -184,29 +187,45 @@ export async function createEnvironmentApiKey(
184187 }
185188 }
186189
187- const prepared = await rbacController . prepareApiKeyPolicy ( {
188- organizationId : environment . organizationId ,
189- presetId,
190- taskIdentifiers : selectedTasks . length > 0 ? selectedTasks : undefined ,
191- } ) ;
190+ let prepared : Awaited < ReturnType < typeof rbacController . prepareApiKeyPolicy > > ;
191+ try {
192+ prepared = await rbacController . prepareApiKeyPolicy ( {
193+ organizationId : environment . organizationId ,
194+ presetId,
195+ taskIdentifiers : selectedTasks . length > 0 ? selectedTasks : undefined ,
196+ } ) ;
197+ } catch ( error ) {
198+ telemetryRecorder . recordOperation ( "prepare_policy" , "error" , "policy_error" ) ;
199+ throw error ;
200+ }
192201
193202 if ( ! prepared . ok ) {
203+ telemetryRecorder . recordOperation ( "prepare_policy" , "rejected" , "policy_rejected" ) ;
194204 throw new Error ( prepared . error ) ;
195205 }
206+ telemetryRecorder . recordOperation ( "prepare_policy" , "success" ) ;
196207
197208 const generated = generateAdditionalApiKey ( environment . type ) ;
198- const apiKey = await prismaClient . apiKey . create ( {
199- data : {
200- name,
201- keyHash : generated . keyHash ,
202- lastFour : generated . lastFour ,
203- runtimeEnvironmentId : environment . id ,
204- createdByUserId : userId ,
205- expiresAt,
206- presetId : prepared . policy . presetId ,
207- scopes : prepared . policy . scopes ,
208- } ,
209- } ) ;
209+ const apiKey = await ( async ( ) => {
210+ try {
211+ return await prismaClient . apiKey . create ( {
212+ data : {
213+ name,
214+ keyHash : generated . keyHash ,
215+ lastFour : generated . lastFour ,
216+ runtimeEnvironmentId : environment . id ,
217+ createdByUserId : userId ,
218+ expiresAt,
219+ presetId : prepared . policy . presetId ,
220+ scopes : prepared . policy . scopes ,
221+ } ,
222+ } ) ;
223+ } catch ( error ) {
224+ telemetryRecorder . recordOperation ( "create" , "error" , "database_error" ) ;
225+ throw error ;
226+ }
227+ } ) ( ) ;
228+ telemetryRecorder . recordOperation ( "create" , "success" ) ;
210229
211230 crumb ( "environment API key created" , {
212231 apiKeyId : apiKey . id ,
@@ -217,26 +236,44 @@ export async function createEnvironmentApiKey(
217236 return { apiKey, plaintext : generated . apiKey } ;
218237}
219238
220- export async function revokeEnvironmentApiKey ( {
221- environmentId,
222- apiKeyId,
223- } : {
224- environmentId : string ;
225- apiKeyId : string ;
226- } ) {
227- const result = await prisma . apiKey . updateMany ( {
228- where : {
229- id : apiKeyId ,
230- runtimeEnvironmentId : environmentId ,
231- revokedAt : null ,
232- } ,
233- data : { revokedAt : new Date ( ) } ,
234- } ) ;
239+ export async function revokeEnvironmentApiKey (
240+ {
241+ environmentId,
242+ apiKeyId,
243+ } : {
244+ environmentId : string ;
245+ apiKeyId : string ;
246+ } ,
247+ {
248+ prismaClient = prisma ,
249+ telemetryRecorder = apiKeyTelemetry ,
250+ } : {
251+ prismaClient ?: Pick < PrismaClient , "apiKey" > ;
252+ telemetryRecorder ?: ApiKeyTelemetry ;
253+ } = { }
254+ ) {
255+ const result = await ( async ( ) => {
256+ try {
257+ return await prismaClient . apiKey . updateMany ( {
258+ where : {
259+ id : apiKeyId ,
260+ runtimeEnvironmentId : environmentId ,
261+ revokedAt : null ,
262+ } ,
263+ data : { revokedAt : new Date ( ) } ,
264+ } ) ;
265+ } catch ( error ) {
266+ telemetryRecorder . recordOperation ( "revoke" , "error" , "database_error" ) ;
267+ throw error ;
268+ }
269+ } ) ( ) ;
235270
236271 if ( result . count !== 1 ) {
272+ telemetryRecorder . recordOperation ( "revoke" , "rejected" , "not_found_or_revoked" ) ;
237273 throw new Error ( "API key not found or already revoked" ) ;
238274 }
239275
276+ telemetryRecorder . recordOperation ( "revoke" , "success" ) ;
240277 crumb ( "environment API key revoked" , { apiKeyId, environmentId } ) ; // @crumbs
241278}
242279
0 commit comments