Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a780a77
Show raw indexed text in the search debug script
Joker666 Sep 1, 2026
5add3f5
Merge branch 'main' into fix/partner-search-debug-raw
Joker666 Sep 1, 2026
7f58786
Strip the program domain from pasted short links before searching
Joker666 Sep 1, 2026
dc435cf
Cover the list, count and missing-program paths for pasted links
Joker666 Sep 1, 2026
a655c92
Show an error when magic-link login fails instead of a success toast.
devkiran Sep 2, 2026
a1363da
revalidate public partner pages with after()
pepeladeira Sep 2, 2026
007fe63
Merge pull request #4440 from dubinc/fix/revalidate-program-pages-after
steven-tey Sep 2, 2026
51211bd
Merge pull request #4439 from dubinc/fix-magic-link-rate-limit-toast
steven-tey Sep 2, 2026
28888ee
Update customer-subscription-deleted.ts
steven-tey Sep 2, 2026
4a9d33a
Merge pull request #4428 from dubinc/fix/partner-search-debug-raw
steven-tey Sep 2, 2026
8f2c3dc
Merge pull request #4433 from dubinc/improvement/partner-search-strip…
steven-tey Sep 2, 2026
13cd490
persist country on customer PATCH
pepeladeira Sep 2, 2026
83993a7
test improvements
pepeladeira Sep 2, 2026
24b744d
Merge pull request #4441 from dubinc/update-customer-country
pepeladeira Sep 2, 2026
c53cd23
allow creating sale commissions by discount code
pepeladeira Sep 2, 2026
8ad98a0
code improvements
pepeladeira Sep 2, 2026
1eba8c1
Improvements to tracking page
steven-tey Sep 2, 2026
c06aeaa
Update apps/web/lib/zod/schemas/commissions.ts
steven-tey Sep 2, 2026
b0b8673
Merge pull request #4443 from dubinc/improve-tracking-page
steven-tey Sep 2, 2026
d9fa0c8
Merge pull request #4442 from dubinc/create-commission-discount-code
steven-tey Sep 2, 2026
6fb40dd
Update partners-redirect.ts
steven-tey Sep 2, 2026
4989f63
fix cron/domains/update
steven-tey Sep 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/web/app/(ee)/api/bounties/[bountyId]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ export const PATCH = withWorkspace(
archivedAt: data.archivedAt,
});

revalidateProgramPublicPages(programId);

waitUntil(
Promise.allSettled([
recordAuditLog({
Expand Down Expand Up @@ -394,8 +396,6 @@ export const PATCH = withWorkspace(
notBefore: Math.floor(data.startsAt.getTime() / 1000),
}),
}),

revalidateProgramPublicPages(programId),
]),
);

Expand Down Expand Up @@ -453,6 +453,8 @@ export const DELETE = withWorkspace(

const deletedBounty = BountySchema.parse(transformBounty(bounty));

revalidateProgramPublicPages(programId);

waitUntil(
Promise.allSettled([
recordAuditLog({
Expand All @@ -469,8 +471,6 @@ export const DELETE = withWorkspace(
},
],
}),

revalidateProgramPublicPages(programId),
]),
);

Expand Down
6 changes: 3 additions & 3 deletions apps/web/app/(ee)/api/bounties/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { DubApiError } from "@/lib/api/errors";
import { throwIfInvalidGroupIds } from "@/lib/api/groups/throw-if-invalid-group-ids";
import { throwIfInvalidPartnerTagIds } from "@/lib/api/partner-tags/throw-if-invalid-partner-tag-ids";
import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-program-id-or-throw";
import { revalidateProgramPublicPages } from "@/lib/api/programs/revalidate-program-public-pages";
import { getProgramEnrollmentOrThrow } from "@/lib/api/programs/get-program-enrollment-or-throw";
import { revalidateProgramPublicPages } from "@/lib/api/programs/revalidate-program-public-pages";
import { parseRequestBody } from "@/lib/api/utils";
import { WorkflowAction } from "@/lib/api/workflows/types";
import { validateWorkflowConditions } from "@/lib/api/workflows/validate-workflow-conditions";
Expand Down Expand Up @@ -329,6 +329,8 @@ export const POST = withWorkspace(
canSendEmailCampaigns &&
bounty.startMode !== BountyStartMode.relative;

revalidateProgramPublicPages(programId);

waitUntil(
Promise.allSettled([
recordAuditLog({
Expand Down Expand Up @@ -373,8 +375,6 @@ export const POST = withWorkspace(
notBefore: Math.floor(bounty.startsAt.getTime() / 1000),
}),
}),

revalidateProgramPublicPages(programId),
]),
);

Expand Down
31 changes: 17 additions & 14 deletions apps/web/app/(ee)/api/cron/domains/update/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ export async function POST(req: Request) {
const linksToUpdate = await prisma.link.findMany({
where: {
domain: oldDomain,
...(programId && { programId }),
...(programId && {
programId,
key: {
not: "_root", // edge case, but don't update the root link (since there's already a link for it)
},
}),
},
take: LINK_BATCH_SIZE,
...(startingAfter && {
Expand All @@ -65,20 +70,18 @@ export async function POST(req: Request) {

const linkIdsToUpdate = linksToUpdate.map((link) => link.id);

try {
await prisma.link.updateMany({
where: {
id: {
in: linkIdsToUpdate,
},
},
data: {
domain: newDomain,
const { count } = await prisma.link.updateMany({
where: {
id: {
in: linkIdsToUpdate,
},
});
} catch (error) {
console.error(error);
}
},
data: {
domain: newDomain,
},
});

console.log(`Updated ${count} links for domain ${oldDomain}`);

const updatedLinks = await prisma.link.findMany({
where: {
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/(ee)/api/customers/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const PATCH = withWorkspace(
const { includeExpandedFields } =
getCustomersQuerySchema.parse(searchParams);

const { name, email, avatar, externalId, stripeCustomerId } =
const { name, email, avatar, externalId, stripeCustomerId, country } =
updateCustomerBodySchema.parse(await parseRequestBody(req));

const customer = await getCustomerOrThrow(
Expand Down Expand Up @@ -84,6 +84,7 @@ export const PATCH = withWorkspace(
avatar: finalCustomerAvatar,
externalId,
stripeCustomerId,
country,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { DEFAULT_PARTNER_GROUP, GroupSchema } from "@/lib/zod/schemas/groups";
import { RESOURCE_COLORS } from "@/ui/colors";
import { nanoid, randomValue } from "@dub/utils";
import slugify from "@sindresorhus/slugify";
import { waitUntil } from "@vercel/functions";
import { NextResponse } from "next/server";

// POST /api/groups/[groupIdOrSlug]/default – set a group as default
Expand Down Expand Up @@ -85,7 +84,7 @@ export const POST = withWorkspace(
});
});

waitUntil(revalidateProgramPublicPages(programId));
revalidateProgramPublicPages(programId);

return NextResponse.json(GroupSchema.parse(updatedGroup));
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export async function customerSubscriptionDeleted(
"`* deleted their *`" +
capitalize(workspace.plan) +
"`* subscription",
type: "cron",
type: "alerts",
mention: true,
}),

Expand Down
1 change: 1 addition & 0 deletions apps/web/app/api/links/sync/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const POST = withWorkspace(
domain: link.domain,
key: link.key,
},
projectId: null,
userId: null,
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { X } from "@/ui/shared/icons";
import { Button, Modal, useMediaQuery } from "@dub/ui";
import { cn, validDomainRegex } from "@dub/utils";
import { useCallback, useMemo, useState } from "react";
import { useState } from "react";
import { toast } from "sonner";

const isValidHostname = (hostname: string) => {
Expand All @@ -14,87 +14,54 @@ const isValidHostname = (hostname: string) => {
);
};

const AddHostnameForm = ({
const AddHostnameModal = ({
showModal,
setShowModal,
existingHostnames,
onAdd,
onCancel,
}: {
showModal: boolean;
setShowModal: (showModal: boolean) => void;
existingHostnames: string[];
onAdd: (hostname: string) => void;
onCancel?: () => void;
onAdd: (hostname: string) => void | Promise<void>;
}) => {
const [hostname, setHostname] = useState("");
const [isLoading, setIsLoading] = useState(false);
const { isMobile } = useMediaQuery();

return (
<form
className="bg-neutral-50"
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
const close = () => {
if (isLoading) {
return;
}

if (existingHostnames.includes(hostname)) {
toast.error("Hostname already exists.");
return;
}
setShowModal(false);
};

if (!isValidHostname(hostname)) {
toast.error("Enter a valid domain.");
return;
}
const handleAdd = async () => {
if (isLoading) {
return;
}

onAdd(hostname);
setHostname("");
}}
>
<div className="relative flex-1 rounded-md px-6 py-5">
<input
type="text"
required
value={hostname}
onChange={(e) => setHostname(e.target.value)}
autoComplete="off"
autoFocus={!isMobile}
placeholder="example.com or *.example.com"
className={cn(
"block w-full rounded-md border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm",
)}
/>
</div>
if (existingHostnames.includes(hostname)) {
toast.error("Hostname already exists.");
return;
}

<div className="flex items-center justify-end gap-2 border-t border-neutral-200 px-6 py-5">
<Button
onClick={() => onCancel?.()}
variant="secondary"
text="Cancel"
className="h-8 w-fit px-3"
/>
<Button
type="submit"
variant="primary"
text="Add hostname"
className="h-8 w-fit px-3"
disabled={!isValidHostname(hostname)}
/>
</div>
</form>
);
};
if (!isValidHostname(hostname)) {
toast.error("Enter a valid domain.");
return;
}

interface AddHostnameModalProps {
showModal: boolean;
setShowModal: (showModal: boolean) => void;
existingHostnames: string[];
onAdd: (hostname: string) => void;
}
setIsLoading(true);
try {
await onAdd(hostname);
setHostname("");
setShowModal(false);
} finally {
setIsLoading(false);
}
};

const AddHostnameModal = ({
showModal,
setShowModal,
existingHostnames,
onAdd,
}: AddHostnameModalProps) => {
const close = () => setShowModal(false);
return (
<Modal showModal={showModal} setShowModal={setShowModal}>
<div className="flex items-center justify-between border-b border-neutral-200 p-4">
Expand All @@ -108,16 +75,48 @@ const AddHostnameModal = ({
</button>
</div>

<div className="bg-neutral-50">
<AddHostnameForm
existingHostnames={existingHostnames}
onCancel={close}
onAdd={(hostname) => {
onAdd(hostname);
close();
}}
/>
</div>
<form
className="bg-neutral-50"
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
void handleAdd();
}}
>
<div className="relative flex-1 rounded-md px-6 py-5">
<input
type="text"
required
value={hostname}
onChange={(e) => setHostname(e.target.value)}
autoComplete="off"
autoFocus={!isMobile}
placeholder="example.com or *.example.com"
className={cn(
"block w-full rounded-md border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm",
)}
/>
</div>

<div className="flex items-center justify-end gap-2 border-t border-neutral-200 px-6 py-5">
<Button
type="button"
onClick={close}
variant="secondary"
text="Cancel"
className="h-8 w-fit px-3"
disabled={isLoading}
/>
<Button
type="submit"
variant="primary"
text="Add hostname"
className="h-8 w-fit px-3"
disabled={!isValidHostname(hostname)}
loading={isLoading}
/>
</div>
</form>
</Modal>
);
};
Expand All @@ -127,26 +126,19 @@ export function useAddHostnameModal({
onAdd,
}: {
existingHostnames: string[];
onAdd: (hostname: string) => void;
onAdd: (hostname: string) => void | Promise<void>;
}) {
const [showAddHostnameModal, setShowAddHostnameModal] = useState(false);

const AddHostnameModalCallback = useCallback(() => {
return (
return {
setShowAddHostnameModal,
addHostnameModal: (
<AddHostnameModal
showModal={showAddHostnameModal}
setShowModal={setShowAddHostnameModal}
existingHostnames={existingHostnames}
onAdd={onAdd}
/>
);
}, [showAddHostnameModal, existingHostnames, onAdd]);

return useMemo(
() => ({
setShowAddHostnameModal,
AddHostnameModal: AddHostnameModalCallback,
}),
[AddHostnameModalCallback],
);
),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,11 @@ export function ConfigureTrackingSection() {
<HostnameField
hostnames={field.value}
onChange={field.onChange}
onSave={(allowedHostnames) =>
handleSubmit((data) =>
onSubmit({ ...data, allowedHostnames }),
)()
}
disabled={disabled}
disabledTooltip={disabledTooltip}
/>
Expand Down
Loading
Loading