From b15bb45599e0466c8973d95cdb6a6cb577af7c64 Mon Sep 17 00:00:00 2001 From: "Thomas F. K. Jorna" Date: Mon, 9 Mar 2026 20:23:52 +0100 Subject: [PATCH] fix: cache purge behavior for communities --- .../CommunityCreate/CommunityCreate.tsx | 1 - server/spamTag/api.ts | 17 ++++++++++++++- server/spamTag/communityQueries.ts | 3 +++ server/spamTag/userDashboard.ts | 21 ++++++++++++------- server/spamTag/userQueries.ts | 17 +++++++++++++-- utils/caching/purgeMiddleware.ts | 5 +++++ 6 files changed, 53 insertions(+), 11 deletions(-) diff --git a/client/containers/CommunityCreate/CommunityCreate.tsx b/client/containers/CommunityCreate/CommunityCreate.tsx index 2b2601bd91..8af9657e60 100644 --- a/client/containers/CommunityCreate/CommunityCreate.tsx +++ b/client/containers/CommunityCreate/CommunityCreate.tsx @@ -145,7 +145,6 @@ const CommunityCreate = () => { setHeroLogo(val); }; - console.log('createError', createError); return (
diff --git a/server/spamTag/api.ts b/server/spamTag/api.ts index 539ebeace2..62624639bc 100644 --- a/server/spamTag/api.ts +++ b/server/spamTag/api.ts @@ -5,12 +5,17 @@ import { Router } from 'express'; import { ForbiddenError } from 'server/utils/errors'; import { wrap } from 'server/wrap'; import { expect } from 'utils/assert'; +import { schedulePurge } from 'utils/caching/schedulePurgeWithSentry'; import { queryCommunitiesForSpamManagement } from './communityDashboard'; import { updateSpamTagForCommunity } from './communityQueries'; import { contextFromUser, notify } from './notifications'; import { canManipulateSpamTags } from './permissions'; -import { getRecentDiscussionsForUser, queryUsersForSpamManagement } from './userDashboard'; +import { + getAffiliationForUserIds, + getRecentDiscussionsForUser, + queryUsersForSpamManagement, +} from './userDashboard'; import { getSpamTagForUser, removeSpamTagFromUser, upsertSpamTag } from './userQueries'; export const router = Router(); @@ -59,6 +64,16 @@ router.put( spamFields: spamTag.fields as UserSpamTagFields, }), ); + + // should schedule purges for all communities the user has commented on, ugh + const communities = await getAffiliationForUserIds([userId]); + const communitySubdomains = communities.get(userId)?.communitySubdomains; + if (communitySubdomains) { + for (const communitySubdomain of communitySubdomains) { + schedulePurge(`${communitySubdomain}.pubpub.org`); + } + } + return res.status(200).send({}); }), ); diff --git a/server/spamTag/communityQueries.ts b/server/spamTag/communityQueries.ts index bcb630bf77..7abd4ab842 100644 --- a/server/spamTag/communityQueries.ts +++ b/server/spamTag/communityQueries.ts @@ -8,6 +8,7 @@ import { } from 'server/utils/email/communitySpam'; import { postToSlackAboutCommunityStatusChange } from 'server/utils/slack'; import { expect } from 'utils/assert'; +import { schedulePurge } from 'utils/caching/schedulePurgeWithSentry'; import { getSuspectedCommunitySpamVerdict } from './communityScore'; @@ -98,4 +99,6 @@ export const updateSpamTagForCommunity = async (options: UpdateSpamTagForCommuni subdomain: ctx.communitySubdomain, status, }); + + schedulePurge(`${ctx.communitySubdomain}.pubpub.org`); }; diff --git a/server/spamTag/userDashboard.ts b/server/spamTag/userDashboard.ts index 69aea0923b..e16d99293a 100644 --- a/server/spamTag/userDashboard.ts +++ b/server/spamTag/userDashboard.ts @@ -207,7 +207,7 @@ export const queryUsersForSpamManagement = async ( return plain as SerializedSpamUserRow[]; }; -const getAffiliationForUserIds = async ( +export const getAffiliationForUserIds = async ( userIds: string[], ): Promise> => { const [members, attributions, discussions] = await Promise.all([ @@ -251,22 +251,29 @@ const getAffiliationForUserIds = async ( let discussionCount = 0; for (const m of members) { if (m.userId === uid) { - const comm = (m as any).community; - if (comm?.subdomain) subdomains.add(comm.subdomain); + const subdomain = m['community.subdomain']; + if (subdomain) { + subdomains.add(subdomain); + } } } for (const a of attributions) { if (a.userId === uid) { pubCount += 1; - const pub = (a as any).pub; - if (pub?.community?.subdomain) subdomains.add(pub.community.subdomain); + + const subdomain = a['pub.community.subdomain']; + if (subdomain) { + subdomains.add(subdomain); + } } } for (const d of discussions) { if (d.userId === uid) { discussionCount += 1; - const pub = (d as any).pub; - if (pub?.community?.subdomain) subdomains.add(pub.community.subdomain); + const subdomain = d['pub.community.subdomain']; + if (subdomain) { + subdomains.add(subdomain); + } } } map.set(uid, { diff --git a/server/spamTag/userQueries.ts b/server/spamTag/userQueries.ts index ec621a375e..6725d0d9ba 100644 --- a/server/spamTag/userQueries.ts +++ b/server/spamTag/userQueries.ts @@ -6,7 +6,9 @@ import mergeWith from 'lodash.mergewith'; import { SpamTag, User } from 'server/models'; import { deleteSessionsForUser } from 'server/utils/session'; import { expect } from 'utils/assert'; +import { schedulePurge } from 'utils/caching/schedulePurgeWithSentry'; +import { getAffiliationForUserIds } from './userDashboard'; import { getSuspectedUserSpamVerdict } from './userScore'; const mergeSpamTagFields = ( @@ -58,6 +60,17 @@ const invalidateUserSessions = async (user: User) => { ); }; +const schedulePurgesForUser = async (userId: string) => { + // should schedule purges for all communities the user has commented on, ugh + const communities = await getAffiliationForUserIds([userId]); + const communitySubdomains = communities.get(userId)?.communitySubdomains; + if (communitySubdomains) { + for (const communitySubdomain of communitySubdomains) { + schedulePurge(`${communitySubdomain}.pubpub.org`); + } + } +}; + export const upsertSpamTag = async (options: UpsertSpamTagOptions): Promise => { const { userId, fields, status } = options; const user = await fetchUserWithSpamTag(userId); @@ -73,7 +86,7 @@ export const upsertSpamTag = async (options: UpsertSpamTagOptions): Promise); if (status === 'confirmed-spam' && existingTag.status !== status) { - await invalidateUserSessions(user); + await Promise.all([invalidateUserSessions(user), schedulePurgesForUser(userId)]); } return { spamTag: existingTag, user }; } @@ -85,7 +98,7 @@ export const upsertSpamTag = async (options: UpsertSpamTagOptions): Promise