Skip to content
65 changes: 65 additions & 0 deletions apps/web/app/(ee)/api/cron/cleanup/unenrolled-partners/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { handleAndReturnErrorResponse } from "@/lib/api/errors";
import { bulkDeletePartners } from "@/lib/api/partners/bulk-delete-partners";
import { verifyQstashSignature } from "@/lib/cron/verify-qstash";
import { prisma } from "@dub/prisma";
import { log } from "@dub/utils";
import { logAndRespond } from "../../utils";

export const dynamic = "force-dynamic";

// This route is used to remove partners that are not enrolled in any program
// Runs once every day at 02:00:00 AM UTC (0 2 * * *)
// POST /api/cron/cleanup/unenrolled-partners
export async function POST(req: Request) {
try {
const rawBody = await req.text();

await verifyQstashSignature({
req,
rawBody,
});

let deletedPartnersCount = 0;

while (true) {
const partnersToDelete = await prisma.partner.findMany({
where: {
stripeConnectId: null,
programs: {
none: {},
},
users: {
none: {},
},
},
take: 250,
});

if (partnersToDelete.length === 0) {
console.log("No more partners to delete, skipping...");
break;
}

console.log(`Found ${partnersToDelete.length} partners to delete.`);

if (partnersToDelete.length > 0) {
await bulkDeletePartners({
partnerIds: partnersToDelete.map((partner) => partner.id),
deletePartners: true,
});
deletedPartnersCount += partnersToDelete.length;
}
}

return logAndRespond(
`Deleted ${deletedPartnersCount} partners that were not enrolled in any programs.`,
);
} catch (error) {
await log({
message: `/api/cron/cleanup/unenrolled-partners failed - ${error.message}`,
type: "errors",
});

return handleAndReturnErrorResponse(error);
}
}
22 changes: 11 additions & 11 deletions apps/web/app/(ee)/api/cron/usage/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
APP_DOMAIN_WITH_NGROK,
capitalize,
getAdjustedBillingCycleStart,
getPrettyUrl,
log,
} from "@dub/utils";

Expand Down Expand Up @@ -125,14 +124,13 @@ export const updateUsage = async () => {
root: false,
});

const topFive = topLinks.slice(0, 5);
const topFiveLinkIds = topFive.map(({ link }) => link);
const topLinkIds = topLinks.slice(0, 100).map(({ link }) => link);

const linksMetadata = await prisma.link.findMany({
where: {
projectId: workspace.id,
id: {
in: topFiveLinkIds,
in: topLinkIds,
},
},
select: {
Expand All @@ -141,13 +139,15 @@ export const updateUsage = async () => {
},
});

const topFiveLinks = topFive.map((d) => ({
link:
getPrettyUrl(
linksMetadata.find((l) => l.id === d.link)?.shortLink,
) || d.link,
clicks: d.clicks,
}));
const topFiveLinks = topLinks
.filter((d: { link: string; clicks: number }) =>
linksMetadata.find((l) => l.id === d.link),
)
.slice(0, 5)
.map((d: { link: string; clicks: number }) => ({
link: linksMetadata.find((l) => l.id === d.link)!, // coerce here since we're already filtering out links that don't exist
clicks: d.clicks,
}));

const totalClicks = topLinks.reduce(
(acc, curr) => acc + curr.clicks,
Expand Down
38 changes: 38 additions & 0 deletions apps/web/app/(ee)/api/e2e/bounties/[bountyId]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { withWorkspace } from "@/lib/auth";
import { prisma } from "@dub/prisma";
import { ACME_PROGRAM_ID } from "@dub/utils";
import { NextResponse } from "next/server";
import { assertE2EWorkspace } from "../../guard";

// special endpoint to delete a bounty and all associated submissions (only for e2e tests)
export const DELETE = withWorkspace(async ({ params, workspace }) => {
assertE2EWorkspace(workspace);

const { bountyId } = params;

await prisma.$transaction(async (tx) => {
await tx.bountySubmission.deleteMany({
where: {
bountyId,
programId: ACME_PROGRAM_ID,
},
});
const bounty = await tx.bounty.delete({
where: {
id: bountyId,
programId: ACME_PROGRAM_ID,
},
});

if (bounty.workflowId) {
await tx.workflow.delete({
where: {
id: bounty.workflowId,
programId: ACME_PROGRAM_ID,
},
});
}
});

return NextResponse.json({ id: bountyId });
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { parseWorkflowConfig } from "@/lib/api/workflows/parse-workflow-config";
import { withWorkspace } from "@/lib/auth";
import { WORKFLOW_ACTION_TYPES } from "@/lib/zod/schemas/workflows";
import { prisma } from "@dub/prisma";
import { ACME_PROGRAM_ID } from "@dub/utils";
import { NextResponse } from "next/server";
import { assertE2EWorkspace } from "../../guard";

Expand All @@ -16,7 +17,7 @@ export const POST = withWorkspace(async ({ workspace, params }) => {

try {
const workflow = await prisma.workflow.findUnique({
where: { id: workflowId },
where: { id: workflowId, programId: ACME_PROGRAM_ID },
});

if (!workflow) {
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/(ee)/api/e2e/workflows/[workflowId]/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { withWorkspace } from "@/lib/auth";
import { prisma } from "@dub/prisma";
import { ACME_PROGRAM_ID } from "@dub/utils";
import { NextResponse } from "next/server";
import { assertE2EWorkspace } from "../../guard";

Expand All @@ -12,7 +13,7 @@ export const PATCH = withWorkspace(
const body = await req.json();

const workflow = await prisma.workflow.update({
where: { id: workflowId },
where: { id: workflowId, programId: ACME_PROGRAM_ID },
data: {
disabledAt: body.disabledAt ? new Date(body.disabledAt) : null,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export default async function PartnerAuthLayout(props: {
redirect("/register");
}
return (
<div className="relative grid grid-cols-1 min-[900px]:grid-cols-[440px_minmax(0,1fr)] lg:grid-cols-[595px_minmax(0,1fr)]">
<div className="relative grid min-h-screen min-h-[100dvh] grid-cols-1 min-[900px]:grid-cols-[440px_minmax(0,1fr)] lg:grid-cols-[595px_minmax(0,1fr)]">
<PartnerBanner program={program} />
<SidePanel program={program} />

Expand Down Expand Up @@ -105,7 +105,7 @@ export default async function PartnerAuthLayout(props: {
</div>
))}
</div>
<div className="relative flex h-screen w-full justify-center">
<div className="relative flex min-h-screen min-h-[100dvh] w-full justify-center">
<Logo className="min-[900px]:hidden" />
{props.children}
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/(ee)/partners.dub.co/(auth-other)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function PartnerAuthLayout({
</div>
))}
</div>
<div className="relative flex h-screen w-full justify-center">
<div className="relative flex min-h-screen min-h-[100dvh] w-full justify-center">
<Logo />
{children}
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/(ee)/partners.dub.co/(onboarding)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function PartnerOnboardingLayout({
))}
</div>

<div className="relative flex min-h-screen w-full flex-col items-center justify-between">
<div className="relative flex min-h-screen min-h-[100dvh] w-full flex-col items-center justify-between">
<div className="grow basis-0">
<div className="pt-4">
<Link href="https://dub.co/home" target="_blank" className="block">
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/app.dub.co/(auth)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default function AuthLayout({ children }: { children: ReactNode }) {
<>
<Toolbar />

<div className="relative grid h-screen grid-cols-1 min-[900px]:grid-cols-[minmax(0,1fr)_440px] lg:grid-cols-[minmax(0,1fr)_595px]">
<div className="relative grid min-h-screen min-h-[100dvh] grid-cols-1 min-[900px]:grid-cols-[minmax(0,1fr)_440px] lg:grid-cols-[minmax(0,1fr)_595px]">
{/* Left: Main auth content */}
<div className="relative">
<div className="absolute inset-0 isolate overflow-hidden bg-white">
Expand Down Expand Up @@ -49,7 +49,7 @@ export default function AuthLayout({ children }: { children: ReactNode }) {
))}
</div>

<div className="relative flex min-h-screen w-full justify-center">
<div className="relative flex min-h-screen min-h-[100dvh] w-full justify-center">
<a
href="https://dub.co"
target="_blank"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default function Layout({ children }: PropsWithChildren) {
))}
</div>

<div className="relative flex min-h-screen w-full flex-col items-center justify-between">
<div className="relative flex min-h-screen min-h-[100dvh] w-full flex-col items-center justify-between">
<div className="grow basis-0">
<div className="pt-4">
<Link href="https://dub.co/home" target="_blank" className="block">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default function Welcome() {
<>
<TrackSignup />
<NewBackground showAnimation showGradient={false} />
<div className="relative flex min-h-screen flex-col items-center justify-center">
<div className="relative flex min-h-screen min-h-[100dvh] flex-col items-center justify-center">
<div className="flex max-w-sm flex-col items-center px-4 py-16 text-center">
<div className="animate-slide-up-fade relative flex w-auto items-center justify-center px-6 py-2 [--offset:20px] [animation-duration:1.3s] [animation-fill-mode:both]">
<Gradient className="opacity-10 mix-blend-overlay" />
Expand Down
17 changes: 0 additions & 17 deletions apps/web/app/app.dub.co/(redirects)/links/[key]/page.tsx

This file was deleted.

10 changes: 0 additions & 10 deletions apps/web/app/app.dub.co/(redirects)/links/page.tsx

This file was deleted.

Loading