From 3195aaae813ab15487f4096abc04fd200fceac7a Mon Sep 17 00:00:00 2001 From: Gregor Lohaus Date: Wed, 2 Jul 2025 23:34:42 +0200 Subject: [PATCH] project router --- src/lib/schema/project/project.ts | 11 ++++ src/lib/schema/project/techStack.ts | 11 ++++ src/server/db/schema.ts | 11 +++- src/server/routers/_app.ts | 4 +- src/server/routers/cv/entry.ts | 3 +- src/server/routers/project/index.ts | 72 +++++++++++++++++++++++++ src/server/routers/project/techStack.ts | 62 +++++++++++++++++++++ 7 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 src/lib/schema/project/project.ts create mode 100644 src/lib/schema/project/techStack.ts create mode 100644 src/server/routers/project/index.ts create mode 100644 src/server/routers/project/techStack.ts diff --git a/src/lib/schema/project/project.ts b/src/lib/schema/project/project.ts new file mode 100644 index 0000000..081561f --- /dev/null +++ b/src/lib/schema/project/project.ts @@ -0,0 +1,11 @@ +import { project } from "~/server/db/schema" +import { createInsertSchema, createUpdateSchema, createSelectSchema} from 'drizzle-zod' +import { z } from "zod"; +export const selectSchema = createSelectSchema(project); +export const insertSchema = createInsertSchema(project); +export const updateSchema = createUpdateSchema(project); +export const getSchema = selectSchema.pick({id: true}); +export const updateRouteSchema = z.object({ + by: selectSchema.pick({id:true}), + update: updateSchema + }) diff --git a/src/lib/schema/project/techStack.ts b/src/lib/schema/project/techStack.ts new file mode 100644 index 0000000..de65e35 --- /dev/null +++ b/src/lib/schema/project/techStack.ts @@ -0,0 +1,11 @@ +import { techStack } from "~/server/db/schema" +import { createInsertSchema, createUpdateSchema, createSelectSchema} from 'drizzle-zod' +import { z } from "zod"; +export const selectSchema = createSelectSchema(techStack); +export const insertSchema = createInsertSchema(techStack); +export const updateSchema = createUpdateSchema(techStack); +export const getSchema = selectSchema.pick({id: true}); +export const updateRouteSchema = z.object({ + by: selectSchema.pick({id:true}), + update: updateSchema + }) diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index a52743c..d3e0c7c 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -54,6 +54,7 @@ export const cvEntryRelations = relations(cvEntry, ({one}) => ({ })); export const sourceTypeEnum = pgEnum('source_type',['open','closed']) +export const releaseStatus = pgEnum('release_status',['released','unreleased']) export const stackItemEnum = pgEnum('stack_item',['drizzle','postgres','nextjs','react','servercomponents','php','laravel','reactnative','expo','mysql','nginx','protobuf','grpc']) export const project = createTable( @@ -62,10 +63,18 @@ export const project = createTable( id: d.uuid().primaryKey().notNull(), title: d.varchar({length: 50}).notNull(), sourceType: sourceTypeEnum(), - + releaseStatus: releaseStatus(), + stackId: d.uuid(), }) ) +export const projectRelations = relations(project, ({one}) => ({ + techStack: one(techStack, { + fields: [project.stackId], + references: [techStack.id], + }) +})) + export const techStack = createTable( "tech_stack", (d) => ({ diff --git a/src/server/routers/_app.ts b/src/server/routers/_app.ts index f47bed0..8e14f84 100644 --- a/src/server/routers/_app.ts +++ b/src/server/routers/_app.ts @@ -2,9 +2,11 @@ import type { inferRouterOutputs } from "@trpc/server"; import { router } from "../trpc"; import { CvRouter } from "./cv"; import type { inferReactQueryProcedureOptions } from "@trpc/react-query"; +import { ProjectRouter } from "./project"; export const trpcRouter = router({ - cv: CvRouter + cv: CvRouter, + project: ProjectRouter }) export type TrpcRouter = typeof trpcRouter diff --git a/src/server/routers/cv/entry.ts b/src/server/routers/cv/entry.ts index 2aa9251..bbd1eec 100644 --- a/src/server/routers/cv/entry.ts +++ b/src/server/routers/cv/entry.ts @@ -51,8 +51,7 @@ export const EntryRouter = router({ ) } const { input } = opts; - const entry = await db.insert(cvEntry).values(input).returning().execute() - return entry + return await db.insert(cvEntry).values(input).returning().execute() }), update: publicProcedure .input(Schemas.updateRouteSchema) diff --git a/src/server/routers/project/index.ts b/src/server/routers/project/index.ts new file mode 100644 index 0000000..011d830 --- /dev/null +++ b/src/server/routers/project/index.ts @@ -0,0 +1,72 @@ +import { publicProcedure, router } from "~/server/trpc" +import { db } from "~/server/db"; +import * as Schemas from '~/lib/schema/project/project' +import { eq } from "drizzle-orm"; +import { project } from "~/server/db/schema"; +import { isAdmin } from "~/app/actions"; +import z from "zod"; +import { StackRouter } from "./techStack"; +import { TRPCError, type inferRouterOutputs} from "@trpc/server"; +export const ProjectRouter = router({ + list: publicProcedure.query(async () => { + return await db.query.project.findMany({ + orderBy: (model, {desc}) => desc(model.title), + with: { + techStack: true + } + }); + }), + get: publicProcedure.input(Schemas.getSchema).query(async (opts) => { + const {input} = opts; + const projects = await db.query.project.findMany({ + with: { + techStack: true + }, + where: eq(project.id,input.id), + limit: 1 + }); + if (projects[0] !== undefined) { + return projects[0] + } else { + return null + } + }), + delete: publicProcedure.input(z.string()).mutation(async (opts) => { + let admin = await isAdmin() + if (!admin) { + throw new TRPCError( + { message: "Access denied", code: "FORBIDDEN", } + ) + } + const { input } = opts; + db.delete(project).where(eq(project.id,input)).execute() + }), + create: publicProcedure.input(Schemas.insertSchema).mutation(async (opts) => { + let admin = await isAdmin() + if (!admin) { + throw new TRPCError( + { message: "Access denied", code: "FORBIDDEN", } + ) + } + const { input } = opts; + return await db.insert(project).values(input).returning().execute(); + }), + update: publicProcedure + .input(Schemas.updateRouteSchema) + .mutation(async (opts) => { + let admin = await isAdmin() + if (!admin) { + throw new TRPCError( + { message: "Access denied", code: "FORBIDDEN", } + ) + } + const { input } = opts; + return await db.update(project) + .set(input.update) + .returning() + .where(eq(project.id,input.by.id)) + }), + stack: StackRouter, +}) + +export type ProjectRouterOutputs = inferRouterOutputs diff --git a/src/server/routers/project/techStack.ts b/src/server/routers/project/techStack.ts new file mode 100644 index 0000000..e2e13ea --- /dev/null +++ b/src/server/routers/project/techStack.ts @@ -0,0 +1,62 @@ +import { publicProcedure, router } from "~/server/trpc" +import { db } from "~/server/db"; +import * as Schemas from '~/lib/schema/project/techStack' +import { eq } from "drizzle-orm"; +import { techStack } from "~/server/db/schema"; +import { isAdmin } from "~/app/actions"; +import { TRPCError, type inferRouterOutputs} from "@trpc/server"; +import z from "zod"; +export const StackRouter = router({ + list: publicProcedure.query(async () => { + return await db.query.techStack.findMany(); + }), + get: publicProcedure.input(Schemas.getSchema).query(async (opts) => { + const {input} = opts; + const techStacks = await db.query.techStack.findMany({ + where: eq(techStack.id,input.id), + limit: 1 + }); + if (techStacks[0] !== undefined) { + return techStacks[0] + } else { + return null + } + }), + delete: publicProcedure.input(z.string()).mutation(async (opts) => { + let admin = await isAdmin() + if (!admin) { + throw new TRPCError( + { message: "Access denied", code: "FORBIDDEN", } + ) + } + const { input } = opts; + db.delete(techStack).where(eq(techStack.id,input)).execute() + }), + create: publicProcedure.input(Schemas.insertSchema).mutation(async (opts) => { + let admin = await isAdmin() + if (!admin) { + throw new TRPCError( + { message: "Access denied", code: "FORBIDDEN", } + ) + } + const { input } = opts; + return await db.insert(techStack).values(input).returning().execute(); + }), + update: publicProcedure + .input(Schemas.updateRouteSchema) + .mutation(async (opts) => { + let admin = await isAdmin() + if (!admin) { + throw new TRPCError( + { message: "Access denied", code: "FORBIDDEN", } + ) + } + const { input } = opts; + return await db.update(techStack) + .set(input.update) + .returning() + .where(eq(techStack.id,input.by.id)) + }) +}) + +export type StackRouterOutputs = inferRouterOutputs