-
-
Notifications
You must be signed in to change notification settings - Fork 535
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP env var management API * Add import env var API endpoint * Adding docs and support for using both API keys and PATs when interacting with the env var endpoints * WIP envvar SDK * Uploading env vars in a variety of formats now works * Finish env var endpoints and add resolveEnvVars hook * Add changeset
- Loading branch information
Showing
44 changed files
with
3,271 additions
and
1,090 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
"@trigger.dev/sdk": patch | ||
"trigger.dev": patch | ||
"@trigger.dev/core": patch | ||
--- | ||
|
||
v3: Environment variable management API and SDK, along with resolveEnvVars CLI hook |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 137 additions & 0 deletions
137
apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.$name.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import { ActionFunctionArgs, LoaderFunctionArgs, json } from "@remix-run/server-runtime"; | ||
import { UpdateEnvironmentVariableRequestBody } from "@trigger.dev/core/v3"; | ||
import { z } from "zod"; | ||
import { prisma } from "~/db.server"; | ||
import { | ||
authenticateProjectApiKeyOrPersonalAccessToken, | ||
authenticatedEnvironmentForAuthentication, | ||
} from "~/services/apiAuth.server"; | ||
import { EnvironmentVariablesRepository } from "~/v3/environmentVariables/environmentVariablesRepository.server"; | ||
|
||
const ParamsSchema = z.object({ | ||
projectRef: z.string(), | ||
slug: z.string(), | ||
name: z.string(), | ||
}); | ||
|
||
export async function action({ params, request }: ActionFunctionArgs) { | ||
const parsedParams = ParamsSchema.safeParse(params); | ||
|
||
if (!parsedParams.success) { | ||
return json({ error: "Invalid params" }, { status: 400 }); | ||
} | ||
|
||
const authenticationResult = await authenticateProjectApiKeyOrPersonalAccessToken(request); | ||
|
||
if (!authenticationResult) { | ||
return json({ error: "Invalid or Missing API key" }, { status: 401 }); | ||
} | ||
|
||
const environment = await authenticatedEnvironmentForAuthentication( | ||
authenticationResult, | ||
parsedParams.data.projectRef, | ||
parsedParams.data.slug | ||
); | ||
|
||
// Find the environment variable | ||
const variable = await prisma.environmentVariable.findFirst({ | ||
where: { | ||
key: parsedParams.data.name, | ||
projectId: environment.project.id, | ||
}, | ||
}); | ||
|
||
if (!variable) { | ||
return json({ error: "Environment variable not found" }, { status: 404 }); | ||
} | ||
|
||
const repository = new EnvironmentVariablesRepository(); | ||
|
||
switch (request.method.toUpperCase()) { | ||
case "DELETE": { | ||
const result = await repository.deleteValue(environment.project.id, { | ||
id: variable.id, | ||
environmentId: environment.id, | ||
}); | ||
|
||
if (result.success) { | ||
return json({ success: true }); | ||
} else { | ||
return json({ error: result.error }, { status: 400 }); | ||
} | ||
} | ||
case "PUT": | ||
case "POST": { | ||
const jsonBody = await request.json(); | ||
|
||
const body = UpdateEnvironmentVariableRequestBody.safeParse(jsonBody); | ||
|
||
if (!body.success) { | ||
return json({ error: "Invalid request body", issues: body.error.issues }, { status: 400 }); | ||
} | ||
|
||
const result = await repository.edit(environment.project.id, { | ||
values: [ | ||
{ | ||
value: body.data.value, | ||
environmentId: environment.id, | ||
}, | ||
], | ||
id: variable.id, | ||
keepEmptyValues: true, | ||
}); | ||
|
||
if (result.success) { | ||
return json({ success: true }); | ||
} else { | ||
return json({ error: result.error }, { status: 400 }); | ||
} | ||
} | ||
} | ||
} | ||
|
||
export async function loader({ params, request }: LoaderFunctionArgs) { | ||
const parsedParams = ParamsSchema.safeParse(params); | ||
|
||
if (!parsedParams.success) { | ||
return json({ error: "Invalid params" }, { status: 400 }); | ||
} | ||
|
||
const authenticationResult = await authenticateProjectApiKeyOrPersonalAccessToken(request); | ||
|
||
if (!authenticationResult) { | ||
return json({ error: "Invalid or Missing API key" }, { status: 401 }); | ||
} | ||
|
||
const environment = await authenticatedEnvironmentForAuthentication( | ||
authenticationResult, | ||
parsedParams.data.projectRef, | ||
parsedParams.data.slug | ||
); | ||
|
||
// Find the environment variable | ||
const variable = await prisma.environmentVariable.findFirst({ | ||
where: { | ||
key: parsedParams.data.name, | ||
projectId: environment.project.id, | ||
}, | ||
}); | ||
|
||
if (!variable) { | ||
return json({ error: "Environment variable not found" }, { status: 404 }); | ||
} | ||
|
||
const repository = new EnvironmentVariablesRepository(); | ||
|
||
const variables = await repository.getEnvironment(environment.project.id, environment.id, true); | ||
|
||
const environmentVariable = variables.find((v) => v.key === parsedParams.data.name); | ||
|
||
if (!environmentVariable) { | ||
return json({ error: "Environment variable not found" }, { status: 404 }); | ||
} | ||
|
||
return json({ | ||
value: environmentVariable.value, | ||
}); | ||
} |
84 changes: 84 additions & 0 deletions
84
apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { ActionFunctionArgs, json } from "@remix-run/server-runtime"; | ||
import { ImportEnvironmentVariablesRequestBody } from "@trigger.dev/core/v3"; | ||
import { parse } from "dotenv"; | ||
import { z } from "zod"; | ||
import { | ||
authenticateProjectApiKeyOrPersonalAccessToken, | ||
authenticatedEnvironmentForAuthentication, | ||
} from "~/services/apiAuth.server"; | ||
import { EnvironmentVariablesRepository } from "~/v3/environmentVariables/environmentVariablesRepository.server"; | ||
|
||
const ParamsSchema = z.object({ | ||
projectRef: z.string(), | ||
slug: z.string(), | ||
}); | ||
|
||
export async function action({ params, request }: ActionFunctionArgs) { | ||
const parsedParams = ParamsSchema.safeParse(params); | ||
|
||
if (!parsedParams.success) { | ||
return json({ error: "Invalid params" }, { status: 400 }); | ||
} | ||
|
||
const authenticationResult = await authenticateProjectApiKeyOrPersonalAccessToken(request); | ||
|
||
if (!authenticationResult) { | ||
return json({ error: "Invalid or Missing API key" }, { status: 401 }); | ||
} | ||
|
||
const environment = await authenticatedEnvironmentForAuthentication( | ||
authenticationResult, | ||
parsedParams.data.projectRef, | ||
parsedParams.data.slug | ||
); | ||
|
||
const repository = new EnvironmentVariablesRepository(); | ||
|
||
const body = await parseImportBody(request); | ||
|
||
const result = await repository.create(environment.project.id, { | ||
override: typeof body.override === "boolean" ? body.override : false, | ||
environmentIds: [environment.id], | ||
variables: Object.entries(body.variables).map(([key, value]) => ({ | ||
key, | ||
value, | ||
})), | ||
}); | ||
|
||
if (result.success) { | ||
return json({ success: true }); | ||
} else { | ||
return json({ error: result.error, variableErrors: result.variableErrors }, { status: 400 }); | ||
} | ||
} | ||
|
||
async function parseImportBody(request: Request): Promise<ImportEnvironmentVariablesRequestBody> { | ||
const contentType = request.headers.get("content-type") ?? "application/json"; | ||
|
||
if (contentType.includes("multipart/form-data")) { | ||
const formData = await request.formData(); | ||
|
||
const file = formData.get("variables"); | ||
const override = formData.get("override") === "true"; | ||
|
||
if (file instanceof File) { | ||
const buffer = await file.arrayBuffer(); | ||
|
||
const variables = parse(Buffer.from(buffer)); | ||
|
||
return { variables, override }; | ||
} else { | ||
throw json({ error: "Invalid file" }, { status: 400 }); | ||
} | ||
} else { | ||
const rawBody = await request.json(); | ||
|
||
const body = ImportEnvironmentVariablesRequestBody.safeParse(rawBody); | ||
|
||
if (!body.success) { | ||
throw json({ error: "Invalid body" }, { status: 400 }); | ||
} | ||
|
||
return body.data; | ||
} | ||
} |
Oops, something went wrong.