Skip to content

Commit 9e0933e

Browse files
authored
fix: syncing competitive approvals
1 parent 32790ef commit 9e0933e

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

react_main/src/pages/Policy/Moderation/commands.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,26 @@ export function useModCommands(argValues, commandRan, setResults) {
932932
.catch(errorAlert);
933933
},
934934
},
935+
"Sync Competitive Approvals": {
936+
perm: "adjustMinGames",
937+
category: "Site Management",
938+
args: [],
939+
run: function () {
940+
axios
941+
.post("/api/mod/syncCompetitiveApprovals")
942+
.then((res) => {
943+
const count = res.data.restored;
944+
siteInfo.showAlert(
945+
count > 0
946+
? `Restored Competitive Player group to ${count} user(s) who had lost it.`
947+
: "No users needed restoration; all qualifying users already have Competitive Player.",
948+
"success"
949+
);
950+
commandRan();
951+
})
952+
.catch(errorAlert);
953+
},
954+
},
935955
"Toggle Ranked Setup": {
936956
perm: "approveRanked",
937957
category: "Setup Management",

routes/auth.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,34 @@ async function authSuccess(req, uid, email, discordProfile) {
514514
}
515515
);
516516
}
517+
518+
// When auto-approval is on, ensure returning users who qualify get Competitive Player.
519+
// This heals users who may have lost it (e.g. from cache/DB issues) and keeps them in sync.
520+
if (await redis.getAutoApprovalEnabled()) {
521+
const fullUser = await models.User.findOne({ id, deleted: false }).select("_id flagged");
522+
if (fullUser && !fullUser.flagged) {
523+
const rankedGroup = await models.Group.findOne({ name: "Ranked Player" }).select("_id");
524+
const competitiveGroup = await models.Group.findOne({ name: "Competitive Player" }).select("_id");
525+
if (rankedGroup && competitiveGroup) {
526+
const inRanked = await models.InGroup.findOne({
527+
user: fullUser._id,
528+
group: rankedGroup._id,
529+
});
530+
const inCompetitive = await models.InGroup.findOne({
531+
user: fullUser._id,
532+
group: competitiveGroup._id,
533+
});
534+
if (inRanked && !inCompetitive) {
535+
const inCompetitiveGroup = new models.InGroup({
536+
user: fullUser._id,
537+
group: competitiveGroup._id,
538+
});
539+
await inCompetitiveGroup.save();
540+
await redis.cacheUserPermissions(id);
541+
}
542+
}
543+
}
544+
}
517545
}
518546

519547
req.session.user = {

routes/mod.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3788,4 +3788,65 @@ router.post("/autoApproval", async function (req, res) {
37883788
}
37893789
});
37903790

3791+
router.post("/syncCompetitiveApprovals", async function (req, res) {
3792+
try {
3793+
var userId = await routeUtils.verifyLoggedIn(req);
3794+
3795+
if (!(await routeUtils.verifyPermission(res, userId, "adjustMinGames")))
3796+
return;
3797+
3798+
if (!(await redis.getAutoApprovalEnabled())) {
3799+
res.status(400).send(
3800+
"Auto-approval must be enabled. This sync only adds Competitive Player to users who have Ranked Player and would have been auto-approved."
3801+
);
3802+
return;
3803+
}
3804+
3805+
const rankedGroup = await models.Group.findOne({ name: "Ranked Player" }).select("_id");
3806+
const competitiveGroup = await models.Group.findOne({ name: "Competitive Player" }).select("_id");
3807+
if (!rankedGroup || !competitiveGroup) {
3808+
res.status(500).send("Required groups not found.");
3809+
return;
3810+
}
3811+
3812+
const inRanked = await models.InGroup.find({ group: rankedGroup._id })
3813+
.select("user")
3814+
.lean();
3815+
const rankedUserIds = inRanked.map((r) => r.user);
3816+
const inCompetitive = await models.InGroup.find({
3817+
group: competitiveGroup._id,
3818+
user: { $in: rankedUserIds },
3819+
})
3820+
.select("user")
3821+
.lean();
3822+
const competitiveUserIds = new Set(inCompetitive.map((c) => String(c.user)));
3823+
3824+
const usersToAdd = await models.User.find({
3825+
_id: { $in: rankedUserIds },
3826+
deleted: false,
3827+
flagged: { $ne: true },
3828+
})
3829+
.select("_id id")
3830+
.lean();
3831+
3832+
let restored = 0;
3833+
for (const user of usersToAdd) {
3834+
if (!competitiveUserIds.has(String(user._id))) {
3835+
await models.InGroup.create({
3836+
user: user._id,
3837+
group: competitiveGroup._id,
3838+
});
3839+
await redis.cacheUserPermissions(user.id);
3840+
restored++;
3841+
}
3842+
}
3843+
3844+
routeUtils.createModAction(userId, "Sync Competitive Approvals", [restored]);
3845+
res.json({ restored });
3846+
} catch (e) {
3847+
logger.error(e);
3848+
res.status(500).send("Error syncing competitive approvals.");
3849+
}
3850+
});
3851+
37913852
module.exports = router;

0 commit comments

Comments
 (0)