-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Support multiple handles per platform type for partners #4109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
0feb85b
560be53
01f2257
b0caf2b
fb7746e
5fe4998
a0b815c
7dd04a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,21 +24,14 @@ export const GET = withAdmin(async ({ params }) => { | |
| (platform) => platform.verifiedAt !== null, | ||
| ); | ||
|
|
||
| // a partner has at most one platform per type, so there is at most one website | ||
| const websitePlatform = verifiedPlatforms.find( | ||
| (platform) => platform.type === "website", | ||
| ); | ||
|
|
||
| const websiteDomain = websitePlatform | ||
| ? getDomainWithoutWWW(websitePlatform.identifier) ?? null | ||
| : null; | ||
|
|
||
| const matchConditions: Prisma.PartnerPlatformWhereInput[] = []; | ||
|
|
||
| for (const platform of verifiedPlatforms) { | ||
| if (platform.type === "website") { | ||
| // website identifiers are full URLs with inconsistent normalization, | ||
| // so match on the domain instead of the exact identifier | ||
| const websiteDomain = getDomainWithoutWWW(platform.identifier); | ||
|
|
||
| if (websiteDomain) { | ||
| matchConditions.push({ | ||
| identifier: { | ||
|
|
@@ -88,18 +81,25 @@ export const GET = withAdmin(async ({ params }) => { | |
|
|
||
| const sharedPlatforms = verifiedPlatforms | ||
| .map((platform) => { | ||
| let platformMatches = sharedPlatformMatches.filter( | ||
| (match) => match.type === platform.type, | ||
| ); | ||
| let platformMatches: typeof sharedPlatformMatches; | ||
|
|
||
| // "contains" matches on the domain need exact verification | ||
| // (e.g. contains "example.com" would also match "notexample.com") | ||
| if (platform.type === "website") { | ||
| platformMatches = platformMatches.filter( | ||
| // "contains" matches on the domain need exact verification | ||
| // (e.g. contains "example.com" would also match "notexample.com") | ||
| const websiteDomain = getDomainWithoutWWW(platform.identifier); | ||
|
|
||
| platformMatches = sharedPlatformMatches.filter( | ||
| (match) => | ||
| match.type === platform.type && | ||
| websiteDomain !== null && | ||
| getDomainWithoutWWW(match.identifier) === websiteDomain, | ||
| ); | ||
| } else { | ||
| platformMatches = sharedPlatformMatches.filter( | ||
| (match) => | ||
| match.type === platform.type && | ||
| match.identifier === platform.identifier, | ||
| ); | ||
|
Comment on lines
+87
to
+105
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Don’t apply the global Website candidates are fetched with broad 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| return { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| import { withCron } from "@/lib/cron/with-cron"; | ||
| import { prisma } from "@/lib/prisma"; | ||
| import { MAX_PLATFORMS_PER_TYPE } from "@/lib/social-utils"; | ||
| import { logAndRespond } from "../../utils"; | ||
|
|
||
| export const dynamic = "force-dynamic"; | ||
|
|
||
| /** | ||
| * Safety-net cron that enforces the per-type handle limit in case the | ||
| * application-level guard is ever bypassed (there is no DB-level unique key). | ||
| * For any (partnerId, type) group over the limit, it keeps the handles to | ||
| * retain — verified first, then oldest — and deletes the most-recently-added | ||
| * excess. | ||
| * | ||
| * Runs daily. GET /api/cron/partner-platforms/enforce-limits | ||
| */ | ||
| export const GET = withCron(async () => { | ||
| const overLimitGroups = await prisma.partnerPlatform.groupBy({ | ||
| by: ["partnerId", "type"], | ||
| _count: { | ||
| id: true, | ||
| }, | ||
| having: { | ||
| id: { | ||
| _count: { | ||
| gt: MAX_PLATFORMS_PER_TYPE, | ||
| }, | ||
| }, | ||
| }, | ||
| orderBy: { | ||
| partnerId: "asc", | ||
| }, | ||
| take: 100, | ||
| }); | ||
|
|
||
| if (overLimitGroups.length === 0) { | ||
| return logAndRespond("No partners over the per-type platform limit."); | ||
| } | ||
|
|
||
| let trimmedRows = 0; | ||
|
|
||
| for (const { partnerId, type } of overLimitGroups) { | ||
| const platforms = await prisma.partnerPlatform.findMany({ | ||
| where: { | ||
| partnerId, | ||
| type, | ||
| }, | ||
| select: { | ||
| id: true, | ||
| verifiedAt: true, | ||
| createdAt: true, | ||
| }, | ||
| }); | ||
|
|
||
| // Retain verified handles first, then the oldest; delete the rest | ||
| const sortedPlatforms = platforms.sort((a, b) => { | ||
| const aVerified = a.verifiedAt ? 1 : 0; | ||
| const bVerified = b.verifiedAt ? 1 : 0; | ||
|
|
||
| if (aVerified !== bVerified) { | ||
| return bVerified - aVerified; | ||
| } | ||
|
|
||
| return a.createdAt.getTime() - b.createdAt.getTime(); | ||
| }); | ||
|
|
||
| const excessIds = sortedPlatforms | ||
| .slice(MAX_PLATFORMS_PER_TYPE) | ||
| .map((r) => r.id); | ||
|
|
||
| if (excessIds.length > 0) { | ||
| await prisma.partnerPlatform.deleteMany({ | ||
| where: { | ||
| id: { | ||
| in: excessIds, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| trimmedRows += excessIds.length; | ||
|
|
||
| console.log( | ||
| `Trimmed ${excessIds.length} excess ${type} handle(s) for partner ${partnerId}.`, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| return logAndRespond( | ||
| `Enforced per-type platform limit: trimmed ${trimmedRows} row(s) across ${overLimitGroups.length} group(s).`, | ||
| ); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.