From 82b747c0335207e0b2f8638ba490c054ac6f1b64 Mon Sep 17 00:00:00 2001 From: KMKoushik Date: Sat, 2 Nov 2024 09:00:28 +1100 Subject: [PATCH] fixes --- apps/web/src/server/api/routers/api.ts | 8 +- apps/web/src/server/api/routers/domain.ts | 94 +++++++++---------- apps/web/src/server/api/routers/email.ts | 66 ++++++------- apps/web/src/server/api/trpc.ts | 57 +++++++---- apps/web/src/server/public-api/api-utils.ts | 8 ++ .../public-api/api/emails/cancel-email.ts | 4 +- .../public-api/api/emails/update-email.ts | 5 +- 7 files changed, 135 insertions(+), 107 deletions(-) diff --git a/apps/web/src/server/api/routers/api.ts b/apps/web/src/server/api/routers/api.ts index 0f9de3b..f5b9910 100644 --- a/apps/web/src/server/api/routers/api.ts +++ b/apps/web/src/server/api/routers/api.ts @@ -34,9 +34,7 @@ export const apiRouter = createTRPCRouter({ return keys; }), - deleteApiKey: teamProcedure - .input(z.object({ id: z.number() })) - .mutation(async ({ input }) => { - return deleteApiKey(input.id); - }), + deleteApiKey: apiKeyProcedure.mutation(async ({ input }) => { + return deleteApiKey(input.id); + }), }); diff --git a/apps/web/src/server/api/routers/domain.ts b/apps/web/src/server/api/routers/domain.ts index 8f5b0e2..d94a259 100644 --- a/apps/web/src/server/api/routers/domain.ts +++ b/apps/web/src/server/api/routers/domain.ts @@ -4,6 +4,7 @@ import { createTRPCRouter, teamProcedure, protectedProcedure, + domainProcedure, } from "~/server/api/trpc"; import { db } from "~/server/db"; import { @@ -27,14 +28,12 @@ export const domainRouter = createTRPCRouter({ return createDomain(ctx.team.id, input.name, input.region); }), - startVerification: teamProcedure - .input(z.object({ id: z.number() })) - .mutation(async ({ ctx, input }) => { - await ctx.db.domain.update({ - where: { id: input.id }, - data: { isVerifying: true }, - }); - }), + startVerification: domainProcedure.mutation(async ({ ctx, input }) => { + await ctx.db.domain.update({ + where: { id: input.id }, + data: { isVerifying: true }, + }); + }), domains: teamProcedure.query(async ({ ctx }) => { const domains = await db.domain.findMany({ @@ -49,16 +48,13 @@ export const domainRouter = createTRPCRouter({ return domains; }), - getDomain: teamProcedure - .input(z.object({ id: z.number() })) - .query(async ({ input }) => { - return getDomain(input.id); - }), + getDomain: domainProcedure.query(async ({ input }) => { + return getDomain(input.id); + }), - updateDomain: teamProcedure + updateDomain: domainProcedure .input( z.object({ - id: z.number(), clickTracking: z.boolean().optional(), openTracking: z.boolean().optional(), }) @@ -70,43 +66,39 @@ export const domainRouter = createTRPCRouter({ }); }), - deleteDomain: teamProcedure - .input(z.object({ id: z.number() })) - .mutation(async ({ input }) => { - await deleteDomain(input.id); - return { success: true }; - }), + deleteDomain: domainProcedure.mutation(async ({ input }) => { + await deleteDomain(input.id); + return { success: true }; + }), - sendTestEmailFromDomain: teamProcedure - .input(z.object({ id: z.number() })) - .mutation( - async ({ - ctx: { - session: { user }, - team, - }, - input, - }) => { - const domain = await db.domain.findFirst({ - where: { id: input.id, teamId: team.id }, - }); + sendTestEmailFromDomain: domainProcedure.mutation( + async ({ + ctx: { + session: { user }, + team, + }, + input, + }) => { + const domain = await db.domain.findFirst({ + where: { id: input.id, teamId: team.id }, + }); - if (!domain) { - throw new Error("Domain not found"); - } - - if (!user.email) { - throw new Error("User email not found"); - } - - return sendEmail({ - teamId: team.id, - to: user.email, - from: `hello@${domain.name}`, - subject: "Unsend test email", - text: "hello,\n\nUnsend is the best open source sending platform\n\ncheck out https://unsend.dev", - html: "

hello,

Unsend is the best open source sending platform

check out unsend.dev", - }); + if (!domain) { + throw new Error("Domain not found"); } - ), + + if (!user.email) { + throw new Error("User email not found"); + } + + return sendEmail({ + teamId: team.id, + to: user.email, + from: `hello@${domain.name}`, + subject: "Unsend test email", + text: "hello,\n\nUnsend is the best open source sending platform\n\ncheck out https://unsend.dev", + html: "

hello,

Unsend is the best open source sending platform

check out unsend.dev", + }); + } + ), }); diff --git a/apps/web/src/server/api/routers/email.ts b/apps/web/src/server/api/routers/email.ts index f195a76..ef27357 100644 --- a/apps/web/src/server/api/routers/email.ts +++ b/apps/web/src/server/api/routers/email.ts @@ -2,7 +2,11 @@ import { EmailStatus } from "@prisma/client"; import { format, subDays } from "date-fns"; import { z } from "zod"; -import { createTRPCRouter, teamProcedure } from "~/server/api/trpc"; +import { + createTRPCRouter, + emailProcedure, + teamProcedure, +} from "~/server/api/trpc"; import { db } from "~/server/db"; import { cancelEmail, updateEmail } from "~/server/service/email-service"; @@ -167,43 +171,39 @@ export const emailRouter = createTRPCRouter({ return { emailStatusCounts, totalCount, emailDailyStatusCounts }; }), - getEmail: teamProcedure - .input(z.object({ id: z.string() })) - .query(async ({ input }) => { - const email = await db.email.findUnique({ - where: { - id: input.id, - }, - select: { - emailEvents: { - orderBy: { - status: "desc", - }, + getEmail: emailProcedure.query(async ({ input }) => { + const email = await db.email.findUnique({ + where: { + id: input.id, + }, + select: { + emailEvents: { + orderBy: { + status: "desc", }, - id: true, - createdAt: true, - latestStatus: true, - subject: true, - to: true, - from: true, - domainId: true, - text: true, - html: true, - scheduledAt: true, }, - }); + id: true, + createdAt: true, + latestStatus: true, + subject: true, + to: true, + from: true, + domainId: true, + text: true, + html: true, + scheduledAt: true, + }, + }); - return email; - }), + return email; + }), - cancelEmail: teamProcedure - .input(z.object({ id: z.string() })) - .mutation(async ({ input }) => { - await cancelEmail(input.id); - }), + cancelEmail: emailProcedure.mutation(async ({ input }) => { + await cancelEmail(input.id); + }), - updateEmailScheduledAt: teamProcedure - .input(z.object({ id: z.string(), scheduledAt: z.string().datetime() })) + updateEmailScheduledAt: emailProcedure + .input(z.object({ scheduledAt: z.string().datetime() })) .mutation(async ({ input }) => { await updateEmail(input.id, { scheduledAt: input.scheduledAt }); }), diff --git a/apps/web/src/server/api/trpc.ts b/apps/web/src/server/api/trpc.ts index 60151ee..e243b74 100644 --- a/apps/web/src/server/api/trpc.ts +++ b/apps/web/src/server/api/trpc.ts @@ -125,6 +125,45 @@ export const teamProcedure = protectedProcedure.use(async ({ ctx, next }) => { }); }); +export const domainProcedure = teamProcedure + .input(z.object({ id: z.number() })) + .use(async ({ ctx, next, input }) => { + const domain = await db.domain.findUnique({ + where: { id: input.id, teamId: ctx.team.id }, + }); + if (!domain) { + throw new TRPCError({ code: "NOT_FOUND", message: "Domain not found" }); + } + + return next({ ctx: { ...ctx, domain } }); + }); + +export const emailProcedure = teamProcedure + .input(z.object({ id: z.string() })) + .use(async ({ ctx, next, input }) => { + const email = await db.email.findUnique({ + where: { id: input.id, teamId: ctx.team.id }, + }); + if (!email) { + throw new TRPCError({ code: "NOT_FOUND", message: "Email not found" }); + } + + return next({ ctx: { ...ctx, email } }); + }); + +export const apiKeyProcedure = teamProcedure + .input(z.object({ id: z.number() })) + .use(async ({ ctx, next, input }) => { + const apiKey = await db.apiKey.findUnique({ + where: { id: input.id, teamId: ctx.team.id }, + }); + if (!apiKey) { + throw new TRPCError({ code: "NOT_FOUND", message: "API key not found" }); + } + + return next({ ctx: { ...ctx, apiKey } }); + }); + export const contactBookProcedure = teamProcedure .input( z.object({ @@ -133,7 +172,7 @@ export const contactBookProcedure = teamProcedure ) .use(async ({ ctx, next, input }) => { const contactBook = await db.contactBook.findUnique({ - where: { id: input.contactBookId }, + where: { id: input.contactBookId, teamId: ctx.team.id }, }); if (!contactBook) { throw new TRPCError({ @@ -142,13 +181,6 @@ export const contactBookProcedure = teamProcedure }); } - if (contactBook.teamId !== ctx.team.id) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this contact book", - }); - } - return next({ ctx: { ...ctx, contactBook } }); }); @@ -160,7 +192,7 @@ export const campaignProcedure = teamProcedure ) .use(async ({ ctx, next, input }) => { const campaign = await db.campaign.findUnique({ - where: { id: input.campaignId }, + where: { id: input.campaignId, teamId: ctx.team.id }, }); if (!campaign) { throw new TRPCError({ @@ -169,13 +201,6 @@ export const campaignProcedure = teamProcedure }); } - if (campaign.teamId !== ctx.team.id) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this campaign", - }); - } - return next({ ctx: { ...ctx, campaign } }); }); diff --git a/apps/web/src/server/public-api/api-utils.ts b/apps/web/src/server/public-api/api-utils.ts index 485cfca..0e66f9c 100644 --- a/apps/web/src/server/public-api/api-utils.ts +++ b/apps/web/src/server/public-api/api-utils.ts @@ -25,3 +25,11 @@ export const getContactBook = async (c: Context, teamId: number) => { return contactBook; }; + +export const checkIsValidEmailId = async (emailId: string, teamId: number) => { + const email = await db.email.findUnique({ where: { id: emailId, teamId } }); + + if (!email) { + throw new UnsendApiError({ code: "NOT_FOUND", message: "Email not found" }); + } +}; diff --git a/apps/web/src/server/public-api/api/emails/cancel-email.ts b/apps/web/src/server/public-api/api/emails/cancel-email.ts index 87aff3c..7573144 100644 --- a/apps/web/src/server/public-api/api/emails/cancel-email.ts +++ b/apps/web/src/server/public-api/api/emails/cancel-email.ts @@ -2,6 +2,7 @@ import { createRoute, z } from "@hono/zod-openapi"; import { PublicAPIApp } from "~/server/public-api/hono"; import { getTeamFromToken } from "~/server/public-api/auth"; import { cancelEmail } from "~/server/service/email-service"; +import { checkIsValidEmailId } from "../../api-utils"; const route = createRoute({ method: "post", @@ -34,8 +35,9 @@ const route = createRoute({ function cancelScheduledEmail(app: PublicAPIApp) { app.openapi(route, async (c) => { - await getTeamFromToken(c); + const team = await getTeamFromToken(c); const emailId = c.req.param("emailId"); + await checkIsValidEmailId(emailId, team.id); await cancelEmail(emailId); diff --git a/apps/web/src/server/public-api/api/emails/update-email.ts b/apps/web/src/server/public-api/api/emails/update-email.ts index cbf1874..3f4e6cd 100644 --- a/apps/web/src/server/public-api/api/emails/update-email.ts +++ b/apps/web/src/server/public-api/api/emails/update-email.ts @@ -2,6 +2,7 @@ import { createRoute, z } from "@hono/zod-openapi"; import { PublicAPIApp } from "~/server/public-api/hono"; import { getTeamFromToken } from "~/server/public-api/auth"; import { updateEmail } from "~/server/service/email-service"; +import { checkIsValidEmailId } from "../../api-utils"; const route = createRoute({ method: "patch", @@ -44,9 +45,11 @@ const route = createRoute({ function updateEmailScheduledAt(app: PublicAPIApp) { app.openapi(route, async (c) => { - await getTeamFromToken(c); + const team = await getTeamFromToken(c); const emailId = c.req.param("emailId"); + await checkIsValidEmailId(emailId, team.id); + await updateEmail(emailId, { scheduledAt: c.req.valid("json").scheduledAt, });