Skip to content

Commit 96e1426

Browse files
committed
fix: transaction safety, parsePeriodToMs consistency, SQL injection prevention
1 parent 2228c9e commit 96e1426

File tree

2 files changed

+31
-11
lines changed

2 files changed

+31
-11
lines changed

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -336,19 +336,19 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
336336
// ─── Helpers ─────────────────────────────────────────────
337337

338338
function parsePeriodToMs(period: string): number {
339-
const match = period.match(/^(\d+)([hdwm])$/);
339+
const match = period.match(/^(\d+)([mhdw])$/);
340340
if (!match) return 7 * 24 * 60 * 60 * 1000; // default 7d
341341
const [, numStr, unit] = match;
342342
const num = parseInt(numStr, 10);
343343
switch (unit) {
344+
case "m":
345+
return num * 60 * 1000;
344346
case "h":
345347
return num * 60 * 60 * 1000;
346348
case "d":
347349
return num * 24 * 60 * 60 * 1000;
348350
case "w":
349351
return num * 7 * 24 * 60 * 60 * 1000;
350-
case "m":
351-
return num * 30 * 24 * 60 * 60 * 1000;
352352
default:
353353
return 7 * 24 * 60 * 60 * 1000;
354354
}
@@ -1523,7 +1523,9 @@ function MetricsTab({
15231523
to: string | null;
15241524
}) {
15251525
const { values: filterValues } = useSearchParams();
1526-
const versionFilters = filterValues("versions").filter((v) => v !== "");
1526+
const versionFilters = filterValues("versions")
1527+
.map(Number)
1528+
.filter((n) => Number.isInteger(n) && n > 0);
15271529
const models = filterValues("models").filter((v) => v !== "");
15281530
const operations = filterValues("operations").filter((v) => v !== "");
15291531
const providers = filterValues("providers").filter((v) => v !== "");
@@ -1641,7 +1643,9 @@ function VersionPerformanceSection({
16411643
to: string | null;
16421644
}) {
16431645
const { values: filterValues } = useSearchParams();
1644-
const versionFilters = filterValues("versions").filter((v) => v !== "");
1646+
const versionFilters = filterValues("versions")
1647+
.map(Number)
1648+
.filter((n) => Number.isInteger(n) && n > 0);
16451649
const models = filterValues("models").filter((v) => v !== "");
16461650
const operations = filterValues("operations").filter((v) => v !== "");
16471651
const providers = filterValues("providers").filter((v) => v !== "");

apps/webapp/app/v3/services/promptService.server.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,14 @@ export class PromptService extends BaseService {
4848
}
4949
) {
5050
const contentHash = createHash("sha256").update(data.textContent).digest("hex").slice(0, 16);
51-
const nextVersion = await this.#getNextVersionNumber(promptId);
5251

53-
// Remove any existing override, then create new — wraps in transaction
54-
await prisma.$transaction(async (tx) => {
52+
const result = await prisma.$transaction(async (tx) => {
53+
const latest = await tx.promptVersion.findFirst({
54+
where: { promptId },
55+
orderBy: { version: "desc" },
56+
});
57+
const nextVersion = (latest?.version ?? 0) + 1;
58+
5559
await tx.$executeRaw`
5660
UPDATE "prompt_versions"
5761
SET "labels" = array_remove("labels", 'override')
@@ -71,9 +75,11 @@ export class PromptService extends BaseService {
7175
createdBy: data.createdBy,
7276
},
7377
});
78+
79+
return { version: nextVersion };
7480
});
7581

76-
return { version: nextVersion };
82+
return result;
7783
}
7884

7985
async updateOverride(
@@ -127,8 +133,18 @@ export class PromptService extends BaseService {
127133
);
128134
}
129135

130-
await this.#removeLabel(promptId, "override");
131-
await this.#addLabel(versionId, "override");
136+
await prisma.$transaction(async (tx) => {
137+
await tx.$executeRaw`
138+
UPDATE "prompt_versions"
139+
SET "labels" = array_remove("labels", 'override')
140+
WHERE "promptId" = ${promptId} AND 'override' = ANY("labels")
141+
`;
142+
await tx.$executeRaw`
143+
UPDATE "prompt_versions"
144+
SET "labels" = array_append("labels", 'override')
145+
WHERE "id" = ${versionId} AND NOT ('override' = ANY("labels"))
146+
`;
147+
});
132148
}
133149

134150
async #removeLabel(promptId: string, label: string) {

0 commit comments

Comments
 (0)