lifetracker/packages/trpc/routers/colors.ts

112 lines
3.3 KiB
TypeScript

import { experimental_trpcMiddleware, TRPCError } from "@trpc/server";
import { and, desc, eq, inArray, notExists } from "drizzle-orm";
import { z } from "zod";
import { SqliteError } from "@lifetracker/db";
import { colors, calcInverseColor } from "@lifetracker/db/schema";
import {
zColorSchema, ZColor
} from "@lifetracker/shared/types/colors";
import type { Context } from "../index";
import { authedProcedure, router } from "../index";
import { titleCase } from "title-case";
function conditionFromInput(input: { colorName: string }, userId: string) {
return and(eq(titleCase(colors.name), titleCase(input.colorName)), eq(colors.userId, userId));
}
async function createColor(
input: ZColor,
ctx: Context,
) {
return ctx.db.transaction(async (trx) => {
try {
const result = await trx
.insert(colors)
.values({
name: titleCase(input.name),
hexcode: input.hexcode,
inverse: calcInverseColor(input.hexcode),
userId: ctx.user!.id,
})
.returning({
id: colors.id,
name: colors.name,
hexcode: colors.hexcode,
});
return result[0];
} catch (e) {
console.log(e);
if (e instanceof SqliteError) {
if (e.code == "SQLITE_CONSTRAINT_UNIQUE") {
throw new TRPCError({
code: "BAD_REQUEST",
message: "This color already exists",
});
}
}
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Something went wrong",
});
}
});
}
export const colorsAppRouter = router({
list: authedProcedure
.output(z.object({
colors: z.array(
z.object({
id: z.string(),
name: z.string(),
hexcode: z.string(),
userId: z.string(),
}),
),
}))
.query(async ({ ctx }) => {
const dbColors = await ctx.db
.select({
id: colors.id,
name: colors.name,
hexcode: colors.hexcode,
userId: colors.userId,
})
.from(colors);
return {
colors: dbColors.map(({ ...color }) => ({
...color,
})),
};
}),
create: authedProcedure
.input(zColorSchema)
.output(
z.object({
id: z.string(),
hexcode: z.string(),
name: z.string(),
}),
)
.mutation(async ({ input, ctx }) => {
return createColor(input, ctx);
}),
delete: authedProcedure
.input(
z.object({
colorName: z.string(),
}),
)
.mutation(async ({ input, ctx }) => {
const res = await ctx.db
.delete(colors)
.where(conditionFromInput(input, ctx.user.id));
if (res.changes == 0) {
throw new TRPCError({ code: "NOT_FOUND" });
}
}),
});