From 48c5b397eeb3e3903e1ea8b972ec979ba6535107 Mon Sep 17 00:00:00 2001 From: Steven Tey Date: Sat, 19 Apr 2025 18:01:07 -0700 Subject: [PATCH 1/6] Update `shouldPassClickId` implementation --- apps/web/lib/middleware/link.ts | 31 +++++++++++++-------------- apps/web/lib/tinybird/record-click.ts | 6 +++--- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/apps/web/lib/middleware/link.ts b/apps/web/lib/middleware/link.ts index 6e1b4ffbf4a..d31b999e754 100644 --- a/apps/web/lib/middleware/link.ts +++ b/apps/web/lib/middleware/link.ts @@ -167,6 +167,11 @@ export default async function LinkMiddleware( const url = testUrl || cachedLink.url; + // we only pass the clickId if: + // - trackConversion is enabled + // - it's a partner link + const shouldPassClickId = trackConversion || isPartnerLink; + // by default, we only index default dub domain links (e.g. dub.sh) // everything else is not indexed by default, unless the user has explicitly set it to be indexed const shouldIndex = isDubDomain(domain) || doIndex === true; @@ -244,8 +249,8 @@ export default async function LinkMiddleware( const cookieStore = cookies(); let clickId = cookieStore.get(dubIdCookieName)?.value; if (!clickId) { - // if trackConversion is enabled, check if clickId is cached in Redis - if (trackConversion) { + // if we need to pass the clickId, check if clickId is cached in Redis + if (shouldPassClickId) { const ip = process.env.VERCEL === "1" ? ipAddress(req) : LOCALHOST_IP; clickId = (await clickCache.get({ domain, key, ip })) || undefined; @@ -276,7 +281,7 @@ export default async function LinkMiddleware( url, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -297,12 +302,6 @@ export default async function LinkMiddleware( const { country } = process.env.VERCEL === "1" && req.geo ? req.geo : LOCALHOST_GEO_DATA; - // we only pass the clickId if: - // - trackConversion is enabled - // - not a partner link (TODO: add this later) !isPartnerLink - // - there is a clickId - const shouldPassClickId = trackConversion && clickId; - // rewrite to proxy page (/proxy/[domain]/[key]) if it's a bot and proxy is enabled if (isBot && proxy) { return createResponseWithCookies( @@ -330,7 +329,7 @@ export default async function LinkMiddleware( url, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -368,7 +367,7 @@ export default async function LinkMiddleware( url, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -408,7 +407,7 @@ export default async function LinkMiddleware( url: ios, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -442,7 +441,7 @@ export default async function LinkMiddleware( url: android, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -476,7 +475,7 @@ export default async function LinkMiddleware( url: geo[country], webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -510,7 +509,7 @@ export default async function LinkMiddleware( url, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -521,7 +520,7 @@ export default async function LinkMiddleware( headers: new Headers({ destination: getFinalUrl(url, { req, - clickId: trackConversion ? clickId : undefined, + clickId: shouldPassClickId ? clickId : undefined, }), }), }, diff --git a/apps/web/lib/tinybird/record-click.ts b/apps/web/lib/tinybird/record-click.ts index 9c3de151bd6..109be2de1b2 100644 --- a/apps/web/lib/tinybird/record-click.ts +++ b/apps/web/lib/tinybird/record-click.ts @@ -37,7 +37,7 @@ export async function recordClick({ skipRatelimit, timestamp, referrer, - trackConversion, + shouldPassClickId, }: { req: Request; clickId?: string; @@ -50,7 +50,7 @@ export async function recordClick({ skipRatelimit?: boolean; timestamp?: string; referrer?: string; - trackConversion?: boolean; + shouldPassClickId?: boolean; }) { if (!clickId) { return null; @@ -165,7 +165,7 @@ export async function recordClick({ // cache the click data for 5 mins // we're doing this because ingested click events are not available immediately in Tinybird - trackConversion && + shouldPassClickId && redis.set(`clickCache:${clickId}`, clickData, { ex: 60 * 5, }), From 04ac14d7c97bf4ece5e02a38cff159b8742e0e08 Mon Sep 17 00:00:00 2001 From: Steven Tey Date: Sat, 19 Apr 2025 18:02:47 -0700 Subject: [PATCH 2/6] add dub_id + via test --- apps/web/tests/redirects/index.test.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/web/tests/redirects/index.test.ts b/apps/web/tests/redirects/index.test.ts index bc1dea07f7b..07d72ca18b7 100644 --- a/apps/web/tests/redirects/index.test.ts +++ b/apps/web/tests/redirects/index.test.ts @@ -69,6 +69,20 @@ describe.runIf(env.CI)("Link Redirects", async () => { expect(response.status).toBe(302); }); + test("with dub_id and via", async () => { + const response = await fetch(`${h.baseUrl}/track-test`, { + ...fetchOptions, + headers: {}, + }); + + // the location should contain `?dub_id=` query param + expect(response.headers.get("location")).toMatch(/dub_id=[a-zA-Z0-9]+/); + // the location should contain `?via=track-test` query param + expect(response.headers.get("location")).toMatch(/via=track-test/); + expect(response.headers.get("x-powered-by")).toBe(poweredBy); + expect(response.status).toBe(302); + }); + test("with passthrough query", async () => { const response = await fetch( `${h.baseUrl}/checkly-check-passthrough?utm_source=checkly`, @@ -167,7 +181,4 @@ describe.runIf(env.CI)("Link Redirects", async () => { expect(response.headers.get("x-powered-by")).toBe(poweredBy); expect(response.status).toBe(302); }); - - // DUMMY test to record a hit on track-test - await fetch(`${h.baseUrl}/track-test`); }); From 0bd800911fc8e2f4aee77491215b911a76fda391 Mon Sep 17 00:00:00 2001 From: Steven Tey Date: Sat, 19 Apr 2025 18:10:00 -0700 Subject: [PATCH 3/6] fix ts error --- apps/web/app/api/track/click/route.ts | 2 +- apps/web/app/api/track/visit/route.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/web/app/api/track/click/route.ts b/apps/web/app/api/track/click/route.ts index aab53cc9177..3c20131f0b4 100644 --- a/apps/web/app/api/track/click/route.ts +++ b/apps/web/app/api/track/click/route.ts @@ -120,7 +120,7 @@ export const POST = withAxiom(async (req: AxiomRequest) => { workspaceId: cachedLink.projectId, skipRatelimit: true, ...(referrer && { referrer }), - trackConversion: cachedLink.trackConversion, + shouldPassClickId: true, }); } })(), diff --git a/apps/web/app/api/track/visit/route.ts b/apps/web/app/api/track/visit/route.ts index 84fbcd35ed5..133e44187a3 100644 --- a/apps/web/app/api/track/visit/route.ts +++ b/apps/web/app/api/track/visit/route.ts @@ -95,7 +95,6 @@ export const POST = withAxiom(async (req: AxiomRequest) => { workspaceId: cachedLink.projectId, skipRatelimit: true, ...(referrer && { referrer }), - trackConversion: cachedLink.trackConversion, }); } })(), From 16c573e8447b0be4512fb8b00d29b6e9e506ba11 Mon Sep 17 00:00:00 2001 From: Steven Tey Date: Sun, 20 Apr 2025 14:40:33 -0700 Subject: [PATCH 4/6] rearrange tests --- apps/web/tests/redirects/index.test.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/web/tests/redirects/index.test.ts b/apps/web/tests/redirects/index.test.ts index 07d72ca18b7..4617a3c09b9 100644 --- a/apps/web/tests/redirects/index.test.ts +++ b/apps/web/tests/redirects/index.test.ts @@ -55,30 +55,30 @@ describe.runIf(env.CI)("Link Redirects", async () => { expect(response.status).toBe(302); }); - test("with dub_client_reference_id", async () => { - const response = await fetch(`${h.baseUrl}/client_reference_id`, { + test("with dub_id and via", async () => { + const response = await fetch(`${h.baseUrl}/track-test`, { ...fetchOptions, headers: {}, }); - // the location should contain `?client_reference_id=dub_id_` query param - expect(response.headers.get("location")).toMatch( - /client_reference_id=dub_id_[a-zA-Z0-9]+/, - ); + // the location should contain `?dub_id=` query param + expect(response.headers.get("location")).toMatch(/dub_id=[a-zA-Z0-9]+/); + // the location should contain `?via=track-test` query param + expect(response.headers.get("location")).toMatch(/via=track-test/); expect(response.headers.get("x-powered-by")).toBe(poweredBy); expect(response.status).toBe(302); }); - test("with dub_id and via", async () => { - const response = await fetch(`${h.baseUrl}/track-test`, { + test("with dub_client_reference_id", async () => { + const response = await fetch(`${h.baseUrl}/client_reference_id`, { ...fetchOptions, headers: {}, }); - // the location should contain `?dub_id=` query param - expect(response.headers.get("location")).toMatch(/dub_id=[a-zA-Z0-9]+/); - // the location should contain `?via=track-test` query param - expect(response.headers.get("location")).toMatch(/via=track-test/); + // the location should contain `?client_reference_id=dub_id_` query param + expect(response.headers.get("location")).toMatch( + /client_reference_id=dub_id_[a-zA-Z0-9]+/, + ); expect(response.headers.get("x-powered-by")).toBe(poweredBy); expect(response.status).toBe(302); }); From cf6fd835ae033f2783cdb102f5c9358fbd333428 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Mon, 21 Apr 2025 17:25:05 +0530 Subject: [PATCH 5/6] show correct partner-specific rewards in program invite card --- .../app/api/partner-profile/programs/route.ts | 47 ++++++++++++++++++- apps/web/ui/partners/program-card.tsx | 2 +- apps/web/ui/partners/program-invite-card.tsx | 16 ++++++- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/apps/web/app/api/partner-profile/programs/route.ts b/apps/web/app/api/partner-profile/programs/route.ts index 9054364c819..a4bae0c6139 100644 --- a/apps/web/app/api/partner-profile/programs/route.ts +++ b/apps/web/app/api/partner-profile/programs/route.ts @@ -14,8 +14,51 @@ export const GET = withPartnerProfile(async ({ partner, searchParams }) => { program: searchParams.includeRewardsDiscounts ? { include: { - rewards: true, - discounts: true, + rewards: { + where: { + OR: [ + // program-wide rewards + { + partners: { + none: {}, + }, + }, + + // partner-specific rewards + { + partners: { + some: { + programEnrollment: { + partnerId: partner.id, + }, + }, + }, + }, + ], + }, + }, + + discounts: { + where: { + OR: [ + // program-wide discounts + { + programEnrollments: { + none: {}, + }, + }, + + // partner-specific discounts + { + programEnrollments: { + some: { + partnerId: partner.id, + }, + }, + }, + ], + }, + }, }, } : true, diff --git a/apps/web/ui/partners/program-card.tsx b/apps/web/ui/partners/program-card.tsx index 926546e07a8..a6faf84b08c 100644 --- a/apps/web/ui/partners/program-card.tsx +++ b/apps/web/ui/partners/program-card.tsx @@ -47,7 +47,7 @@ export function ProgramCard({ const card = (
diff --git a/apps/web/ui/partners/program-invite-card.tsx b/apps/web/ui/partners/program-invite-card.tsx index 3503dbfc17f..6b68efb972e 100644 --- a/apps/web/ui/partners/program-invite-card.tsx +++ b/apps/web/ui/partners/program-invite-card.tsx @@ -33,6 +33,18 @@ export function ProgramInviteCard({ }, }); + const reward = + program.rewards && program.rewards.length > 0 + ? program.rewards.find((r) => r.id !== program.defaultRewardId) || + program.rewards[0] + : null; + + const discount = + program.discounts && program.discounts.length > 0 + ? program.discounts.find((d) => d.id !== program.defaultDiscountId) || + program.discounts[0] + : null; + return (
@@ -57,8 +69,8 @@ export function ProgramInviteCard({ )}

From df24474eaf1e6fcbb0660ece815390d702c51912 Mon Sep 17 00:00:00 2001 From: Steven Tey Date: Mon, 21 Apr 2025 08:39:14 -0700 Subject: [PATCH 6/6] make website required --- .../app/partners.dub.co/(apply)/[programSlug]/apply/form.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/web/app/partners.dub.co/(apply)/[programSlug]/apply/form.tsx b/apps/web/app/partners.dub.co/(apply)/[programSlug]/apply/form.tsx index 490855d7d9b..c952e61c7be 100644 --- a/apps/web/app/partners.dub.co/(apply)/[programSlug]/apply/form.tsx +++ b/apps/web/app/partners.dub.co/(apply)/[programSlug]/apply/form.tsx @@ -133,7 +133,6 @@ export function ProgramApplicationForm({