Skip to content

Commit fdb4e69

Browse files
committed
WIP env var management API
1 parent a86f36c commit fdb4e69

File tree

6 files changed

+487
-14
lines changed

6 files changed

+487
-14
lines changed

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.environment-variables/route.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ function EditEnvironmentVariablePanel({
334334
name={`values[${index}].value`}
335335
placeholder="Not set"
336336
defaultValue={value}
337+
type="password"
337338
/>
338339
</Fragment>
339340
);
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import { ActionFunctionArgs, LoaderFunctionArgs, json } from "@remix-run/server-runtime";
2+
import { UpdateEnvironmentVariableRequestBody } from "@trigger.dev/core/v3";
3+
import { z } from "zod";
4+
import { prisma } from "~/db.server";
5+
import { findProjectByRef } from "~/models/project.server";
6+
import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server";
7+
import { EnvironmentVariablesRepository } from "~/v3/environmentVariables/environmentVariablesRepository.server";
8+
9+
const ParamsSchema = z.object({
10+
projectRef: z.string(),
11+
slug: z.string(),
12+
name: z.string(),
13+
});
14+
15+
export async function action({ params, request }: ActionFunctionArgs) {
16+
const parsedParams = ParamsSchema.safeParse(params);
17+
18+
if (!parsedParams.success) {
19+
return json({ error: "Invalid params" }, { status: 400 });
20+
}
21+
22+
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
23+
24+
if (!authenticationResult) {
25+
return json({ error: "Invalid or Missing API key" }, { status: 401 });
26+
}
27+
28+
const user = await prisma.user.findUnique({
29+
where: {
30+
id: authenticationResult.userId,
31+
},
32+
});
33+
34+
if (!user) {
35+
return json({ error: "Invalid or Missing API key" }, { status: 401 });
36+
}
37+
38+
const project = await findProjectByRef(parsedParams.data.projectRef, user.id);
39+
40+
if (!project) {
41+
return json({ error: "Project not found" }, { status: 404 });
42+
}
43+
44+
const environment = await prisma.runtimeEnvironment.findFirst({
45+
where: {
46+
projectId: project.id,
47+
slug: parsedParams.data.slug,
48+
},
49+
});
50+
51+
if (!environment) {
52+
return json({ error: "Environment not found" }, { status: 404 });
53+
}
54+
55+
// Find the environment variable
56+
const variable = await prisma.environmentVariable.findFirst({
57+
where: {
58+
key: parsedParams.data.name,
59+
projectId: project.id,
60+
},
61+
});
62+
63+
if (!variable) {
64+
return json({ error: "Environment variable not found" }, { status: 404 });
65+
}
66+
67+
const repository = new EnvironmentVariablesRepository();
68+
69+
switch (request.method.toUpperCase()) {
70+
case "DELETE": {
71+
const result = await repository.deleteValue(project.id, user.id, {
72+
id: variable.id,
73+
environmentId: environment.id,
74+
});
75+
76+
if (result.success) {
77+
return json({ success: true });
78+
} else {
79+
return json({ error: result.error }, { status: 400 });
80+
}
81+
}
82+
case "PUT":
83+
case "POST": {
84+
const jsonBody = await request.json();
85+
86+
const body = UpdateEnvironmentVariableRequestBody.safeParse(jsonBody);
87+
88+
if (!body.success) {
89+
return json({ error: "Invalid request body", issues: body.error.issues }, { status: 400 });
90+
}
91+
92+
const result = await repository.edit(project.id, user.id, {
93+
values: [
94+
{
95+
value: body.data.value,
96+
environmentId: environment.id,
97+
},
98+
],
99+
id: variable.id,
100+
keepEmptyValues: true,
101+
});
102+
103+
if (result.success) {
104+
return json({ success: true });
105+
} else {
106+
return json({ error: result.error }, { status: 400 });
107+
}
108+
}
109+
}
110+
}
111+
112+
export async function loader({ params, request }: LoaderFunctionArgs) {
113+
const parsedParams = ParamsSchema.safeParse(params);
114+
115+
if (!parsedParams.success) {
116+
return json({ error: "Invalid params" }, { status: 400 });
117+
}
118+
119+
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
120+
121+
if (!authenticationResult) {
122+
return json({ error: "Invalid or Missing API key" }, { status: 401 });
123+
}
124+
125+
const user = await prisma.user.findUnique({
126+
where: {
127+
id: authenticationResult.userId,
128+
},
129+
});
130+
131+
if (!user) {
132+
return json({ error: "Invalid or Missing API key" }, { status: 401 });
133+
}
134+
135+
const project = await findProjectByRef(parsedParams.data.projectRef, user.id);
136+
137+
if (!project) {
138+
return json({ error: "Project not found" }, { status: 404 });
139+
}
140+
141+
const environment = await prisma.runtimeEnvironment.findFirst({
142+
where: {
143+
projectId: project.id,
144+
slug: parsedParams.data.slug,
145+
},
146+
});
147+
148+
if (!environment) {
149+
return json({ error: "Environment not found" }, { status: 404 });
150+
}
151+
152+
// Find the environment variable
153+
const variable = await prisma.environmentVariable.findFirst({
154+
where: {
155+
key: parsedParams.data.name,
156+
projectId: project.id,
157+
},
158+
});
159+
160+
if (!variable) {
161+
return json({ error: "Environment variable not found" }, { status: 404 });
162+
}
163+
164+
const repository = new EnvironmentVariablesRepository();
165+
166+
const variables = await repository.getEnvironment(project.id, user.id, environment.id, true);
167+
168+
const environmentVariable = variables.find((v) => v.key === parsedParams.data.name);
169+
170+
if (!environmentVariable) {
171+
return json({ error: "Environment variable not found" }, { status: 404 });
172+
}
173+
174+
return json({
175+
value: environmentVariable.value,
176+
});
177+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { ActionFunctionArgs, LoaderFunctionArgs, json } from "@remix-run/server-runtime";
2+
import { CreateEnvironmentVariableRequestBody } from "@trigger.dev/core/v3";
3+
import { z } from "zod";
4+
import { prisma } from "~/db.server";
5+
import { findProjectByRef } from "~/models/project.server";
6+
import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server";
7+
import { EnvironmentVariablesRepository } from "~/v3/environmentVariables/environmentVariablesRepository.server";
8+
9+
const ParamsSchema = z.object({
10+
projectRef: z.string(),
11+
slug: z.string(),
12+
});
13+
14+
export async function action({ params, request }: ActionFunctionArgs) {
15+
const parsedParams = ParamsSchema.safeParse(params);
16+
17+
if (!parsedParams.success) {
18+
return json({ error: "Invalid params" }, { status: 400 });
19+
}
20+
21+
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
22+
23+
if (!authenticationResult) {
24+
return json({ error: "Invalid or Missing API key" }, { status: 401 });
25+
}
26+
27+
const jsonBody = await request.json();
28+
29+
const body = CreateEnvironmentVariableRequestBody.safeParse(jsonBody);
30+
31+
if (!body.success) {
32+
return json({ error: "Invalid request body", issues: body.error.issues }, { status: 400 });
33+
}
34+
35+
const user = await prisma.user.findUnique({
36+
where: {
37+
id: authenticationResult.userId,
38+
},
39+
});
40+
41+
if (!user) {
42+
return json({ error: "Invalid or Missing API key" }, { status: 401 });
43+
}
44+
45+
const project = await findProjectByRef(parsedParams.data.projectRef, user.id);
46+
47+
if (!project) {
48+
return json({ error: "Project not found" }, { status: 404 });
49+
}
50+
51+
const environment = await prisma.runtimeEnvironment.findFirst({
52+
where: {
53+
projectId: project.id,
54+
slug: parsedParams.data.slug,
55+
},
56+
});
57+
58+
if (!environment) {
59+
return json({ error: "Environment not found" }, { status: 404 });
60+
}
61+
62+
const repository = new EnvironmentVariablesRepository();
63+
64+
const result = await repository.create(project.id, user.id, {
65+
overwrite: true,
66+
environmentIds: [environment.id],
67+
variables: [
68+
{
69+
key: body.data.name,
70+
value: body.data.value,
71+
},
72+
],
73+
});
74+
75+
if (result.success) {
76+
return json({ success: true });
77+
} else {
78+
return json({ error: result.error, variableErrors: result.variableErrors }, { status: 400 });
79+
}
80+
}
81+
82+
export async function loader({ params, request }: LoaderFunctionArgs) {
83+
const parsedParams = ParamsSchema.safeParse(params);
84+
85+
if (!parsedParams.success) {
86+
return json({ error: "Invalid params" }, { status: 400 });
87+
}
88+
89+
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
90+
91+
if (!authenticationResult) {
92+
return json({ error: "Invalid or Missing API key" }, { status: 401 });
93+
}
94+
95+
const user = await prisma.user.findUnique({
96+
where: {
97+
id: authenticationResult.userId,
98+
},
99+
});
100+
101+
if (!user) {
102+
return json({ error: "Invalid or Missing API key" }, { status: 401 });
103+
}
104+
105+
const project = await findProjectByRef(parsedParams.data.projectRef, user.id);
106+
107+
if (!project) {
108+
return json({ error: "Project not found" }, { status: 404 });
109+
}
110+
111+
const environment = await prisma.runtimeEnvironment.findFirst({
112+
where: {
113+
projectId: project.id,
114+
slug: parsedParams.data.slug,
115+
},
116+
});
117+
118+
if (!environment) {
119+
return json({ error: "Environment not found" }, { status: 404 });
120+
}
121+
122+
const repository = new EnvironmentVariablesRepository();
123+
124+
const variables = await repository.getEnvironment(project.id, user.id, environment.id, true);
125+
126+
return json(variables);
127+
}

0 commit comments

Comments
 (0)