feat: better error messages

This commit is contained in:
2026-03-25 14:38:40 +01:00
parent 9ec4c5319c
commit a935d61531
5 changed files with 69 additions and 2 deletions

View File

@@ -17,6 +17,11 @@ export default defineEventHandler(async (event) => {
const params = await getValidatedRouterParams(event, paramsSchema.parse);
const body = await readValidatedBody(event, bodySchema.parse);
const conflicts = await findConflictingFields(body, params.id);
if (conflicts.length > 0) {
throw createError({ statusCode: 409, message: `A link with this ${joinFields(conflicts)} already exists` });
}
const [link] = await db
.update(tables.links)
.set(body)

View File

@@ -11,6 +11,12 @@ const bodySchema = z.object({
export default defineEventHandler(async (event) => {
const body = await readValidatedBody(event, bodySchema.parse);
const conflicts = await findConflictingFields(body);
if (conflicts.length > 0) {
throw createError({ statusCode: 409, message: `A link with this ${joinFields(conflicts)} already exists` });
}
const [link] = await db.insert(tables.links).values(body).returning();
return link;
});

34
server/utils/links.ts Normal file
View File

@@ -0,0 +1,34 @@
import { db } from "#server/db";
import * as tables from "#server/db/schema";
import { and, eq, ne } from "drizzle-orm";
const UNIQUE_FIELDS = ["name", "path", "url"] as const;
type UniqueField = (typeof UNIQUE_FIELDS)[number];
export const joinFields = (fields: string[]): string => {
if (fields.length <= 1) return fields.join("");
return `${fields.slice(0, -1).join(", ")} and ${fields.at(-1)}`;
};
export const findConflictingFields = async (
values: Partial<Record<UniqueField, string>>,
excludeId?: number,
): Promise<UniqueField[]> => {
const conflicts: UniqueField[] = [];
for (const field of UNIQUE_FIELDS) {
const value = values[field];
if (!value) continue;
const conflict = await db.query.links.findFirst({
where: and(
eq(tables.links[field], value),
excludeId !== undefined ? ne(tables.links.id, excludeId) : undefined,
),
});
if (conflict) conflicts.push(field);
}
return conflicts;
};