-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Expand file tree
/
Copy pathbulk-delete-links.ts
More file actions
116 lines (97 loc) · 2.81 KB
/
Copy pathbulk-delete-links.ts
File metadata and controls
116 lines (97 loc) · 2.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { deleteDiscountCodes } from "@/lib/discounts/delete-discount-code";
import { prisma } from "@/lib/prisma";
import { storage } from "@/lib/storage";
import { recordLink } from "@/lib/tinybird";
import { chunk, R2_URL } from "@dub/utils";
import { waitUntil } from "@vercel/functions";
import { linkCache } from "./cache";
import { ExpandedLink } from "./utils";
const DELETE_LINKS_BATCH_SIZE = 100;
/**
* Canonical bulk link deletion:
* 1. Delete related DiscountCodes (and enqueue provider cleanup)
* 2. Delete Link rows + decrement totalLinks (transaction)
* 3. Run side effects (Redis / Tinybird / R2)
*
* Processes links in batches of DELETE_LINKS_BATCH_SIZE.
* Callers must pass links from a single workspace — totalLinks is
* decremented on links[0].projectId for the whole batch.
*/
export async function bulkDeleteLinks(
links: ExpandedLink[],
): Promise<{ deletedCount: number }> {
if (links.length === 0) {
return {
deletedCount: 0,
};
}
let deletedCount = 0;
// Delete links in batches
const batches = chunk(links, DELETE_LINKS_BATCH_SIZE);
for (const [batchIndex, batch] of batches.entries()) {
const batchDeletedCount = await deleteLinksBatch(batch);
deletedCount += batchDeletedCount;
console.log(
`Deleted ${batchDeletedCount} links in batch ${batchIndex + 1}/${batches.length}`,
);
}
if (deletedCount > 0) {
waitUntil(
Promise.allSettled([
// Delete the links from Redis
linkCache.deleteMany(links),
// Record the links deletion in Tinybird
recordLink(links, { deleted: true }),
// For links that have an image, delete the image from R2
...links
.filter((link) =>
link.image?.startsWith(`${R2_URL}/images/${link.id}`),
)
.map((link) =>
storage.delete({ key: link.image!.replace(`${R2_URL}/`, "") }),
),
]),
);
}
return {
deletedCount,
};
}
async function deleteLinksBatch(links: ExpandedLink[]): Promise<number> {
const linkIds = links.map((link) => link.id);
const discountCodes = await prisma.discountCode.findMany({
where: {
linkId: {
in: linkIds,
},
},
include: {
discount: true,
},
});
await deleteDiscountCodes(discountCodes);
const workspaceId = links[0].projectId;
const { count: deletedCount } = await prisma.$transaction(async (tx) => {
const result = await tx.link.deleteMany({
where: {
id: {
in: linkIds,
},
},
});
if (result.count > 0 && workspaceId) {
await tx.project.update({
where: {
id: workspaceId,
},
data: {
totalLinks: {
decrement: result.count,
},
},
});
}
return result;
});
return deletedCount;
}