Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { ActionFunctionArgs, json } from "@remix-run/server-runtime";
import pMap from "p-map";
import { z } from "zod";
import { $replica, prisma } from "~/db.server";
import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server";
import { determineEngineVersion } from "~/v3/engineVersion.server";
import { engine } from "~/v3/runEngine.server";

const ParamsSchema = z.object({
environmentId: z.string(),
});

const BodySchema = z.object({
dryRun: z.boolean().default(true),
queues: z.array(z.string()).default([]),
});

export async function action({ request, params }: ActionFunctionArgs) {
// Next authenticate the request
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);

if (!authenticationResult) {
return json({ error: "Invalid or Missing API key" }, { status: 401 });
}

const user = await prisma.user.findUnique({
where: {
id: authenticationResult.userId,
},
});

if (!user) {
return json({ error: "Invalid or Missing API key" }, { status: 401 });
}

if (!user.admin) {
return json({ error: "You must be an admin to perform this action" }, { status: 403 });
}

const parsedParams = ParamsSchema.parse(params);

const environment = await prisma.runtimeEnvironment.findFirst({
where: {
id: parsedParams.environmentId,
},
Comment on lines +40 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Return 400 on invalid route params instead of throwing (use safeParse).

z.parse will throw and produce a 500 on bad/missing params. Prefer safeParse and return a 400 with issues.

Apply this diff:

-  const parsedParams = ParamsSchema.parse(params);
+  const parsedParamsResult = ParamsSchema.safeParse(params);
+  if (!parsedParamsResult.success) {
+    return json(
+      { error: "Invalid params", issues: parsedParamsResult.error.flatten() },
+      { status: 400 }
+    );
+  }
+  const parsedParams = parsedParamsResult.data;
 
   const environment = await prisma.runtimeEnvironment.findFirst({
     where: {
       id: parsedParams.environmentId,
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const parsedParams = ParamsSchema.parse(params);
const environment = await prisma.runtimeEnvironment.findFirst({
where: {
id: parsedParams.environmentId,
},
const parsedParamsResult = ParamsSchema.safeParse(params);
if (!parsedParamsResult.success) {
return json(
{ error: "Invalid params", issues: parsedParamsResult.error.flatten() },
{ status: 400 }
);
}
const parsedParams = parsedParamsResult.data;
const environment = await prisma.runtimeEnvironment.findFirst({
where: {
id: parsedParams.environmentId,
},
🤖 Prompt for AI Agents
In
apps/webapp/app/routes/admin.api.v1.environments.$environmentId.engine.repair-queues.ts
around lines 40 to 45, the code currently uses ParamsSchema.parse(params) which
will throw on invalid/missing params and surface as a 500; change this to
ParamsSchema.safeParse(params), check the result, and if not successful return a
400 response containing the validation issues (e.g., res.status(400).json({
error: "Invalid route params", issues: result.error.issues })), otherwise
proceed using result.data as parsedParams before calling prisma.

include: {
organization: true,
project: true,
orgMember: true,
},
});

if (!environment) {
return json({ error: "Environment not found" }, { status: 404 });
}

const engineVersion = await determineEngineVersion({ environment });

if (engineVersion === "V1") {
return json({ error: "Engine version is V1" }, { status: 400 });
}

const body = await request.json();
const parsedBody = BodySchema.parse(body);

Comment on lines +63 to +65
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard JSON parsing and validate body with safeParse; avoid 500s on bad/missing JSON.

request.json() throws on empty/invalid JSON, and z.parse throws; both yield 500. Handle both and return 400 with validation details.

Apply this diff:

-  const body = await request.json();
-  const parsedBody = BodySchema.parse(body);
+  let body: unknown;
+  try {
+    body = await request.json();
+  } catch {
+    body = {};
+  }
+  const parsedBodyResult = BodySchema.safeParse(body);
+  if (!parsedBodyResult.success) {
+    return json(
+      { error: "Invalid request body", issues: parsedBodyResult.error.flatten() },
+      { status: 400 }
+    );
+  }
+  const parsedBody = parsedBodyResult.data;

const queues = await $replica.taskQueue.findMany({
where: {
runtimeEnvironmentId: environment.id,
version: "V2",
name: parsedBody.queues.length > 0 ? { in: parsedBody.queues } : undefined,
},
select: {
friendlyId: true,
name: true,
concurrencyLimit: true,
type: true,
paused: true,
},
orderBy: {
orderableName: "asc",
},
});

const repairEnvironmentResults = await engine.repairEnvironment(environment, parsedBody.dryRun);

const repairResults = await pMap(
queues,
async (queue) => {
return engine.repairQueue(environment, queue.name, parsedBody.dryRun);
},
{ concurrency: 5 }
);

return json({ environment: repairEnvironmentResults, queues: repairResults });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { json, LoaderFunctionArgs } from "@remix-run/server-runtime";
import { z } from "zod";
import { $replica, prisma } from "~/db.server";
import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server";
import { determineEngineVersion } from "~/v3/engineVersion.server";
import { engine } from "~/v3/runEngine.server";

const ParamsSchema = z.object({
environmentId: z.string(),
});

const SearchParamsSchema = z.object({
verbose: z.string().default("0"),
page: z.coerce.number().optional(),
per_page: z.coerce.number().optional(),
});

export async function loader({ request, params }: LoaderFunctionArgs) {
// Next authenticate the request
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);

if (!authenticationResult) {
return json({ error: "Invalid or Missing API key" }, { status: 401 });
}

const user = await prisma.user.findUnique({
where: {
id: authenticationResult.userId,
},
});

if (!user) {
return json({ error: "Invalid or Missing API key" }, { status: 401 });
}

if (!user.admin) {
return json({ error: "You must be an admin to perform this action" }, { status: 403 });
}

const parsedParams = ParamsSchema.parse(params);

const environment = await prisma.runtimeEnvironment.findFirst({
where: {
id: parsedParams.environmentId,
},
include: {
organization: true,
project: true,
orgMember: true,
},
});

if (!environment) {
return json({ error: "Environment not found" }, { status: 404 });
}

const engineVersion = await determineEngineVersion({ environment });

if (engineVersion === "V1") {
return json({ error: "Engine version is V1" }, { status: 400 });
}

const url = new URL(request.url);
const searchParams = SearchParamsSchema.parse(Object.fromEntries(url.searchParams));

const page = searchParams.page ?? 1;
const perPage = searchParams.per_page ?? 50;

const queues = await $replica.taskQueue.findMany({
where: {
runtimeEnvironmentId: environment.id,
version: "V2",
},
select: {
friendlyId: true,
name: true,
concurrencyLimit: true,
type: true,
paused: true,
},
orderBy: {
orderableName: "asc",
},
skip: (page - 1) * perPage,
take: perPage,
});

const report = await engine.generateEnvironmentReport(
environment,
queues,
searchParams.verbose === "1"
);

return json(report);
}
3 changes: 2 additions & 1 deletion internal-packages/run-engine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"nanoid": "3.3.8",
"redlock": "5.0.0-beta.2",
"seedrandom": "^3.0.5",
"zod": "3.25.76"
"zod": "3.25.76",
"p-map": "^6.0.0"
},
"devDependencies": {
"@internal/testcontainers": "workspace:*",
Expand Down
Loading