feat: LuckPerms meta sync on Stripe checkout — sets maxclaims/maxchunkloaders across all online servers
This commit is contained in:
@@ -13,6 +13,7 @@ const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
|
||||
const db = require('../database');
|
||||
const { syncRole, removeAllRoles, downgradeToAwakened } = require('../services/discordRoleSync');
|
||||
const { welcomeNewMember } = require('../services/awakenedConcierge');
|
||||
const { syncLuckPermsMeta } = require('../services/luckpermsSync');
|
||||
|
||||
// CORS configuration for checkout endpoint
|
||||
const corsOptions = {
|
||||
@@ -286,6 +287,15 @@ router.post('/webhook', express.raw({ type: 'application/json' }), async (req, r
|
||||
welcomeNewMember(discordId, client).catch(err =>
|
||||
console.error('[Concierge] Background welcome error:', err.message)
|
||||
);
|
||||
|
||||
// Sync LuckPerms meta across all online servers (non-blocking)
|
||||
syncLuckPermsMeta(discordId, tierLevel).then(result => {
|
||||
if (result.success) {
|
||||
console.log(`⚔️ LP meta sync for ${result.username}: tier ${tierLevel}, ${result.serversUpdated} servers`);
|
||||
} else {
|
||||
console.log(`⚠️ LP meta sync skipped for ${discordId}: ${result.reason}`);
|
||||
}
|
||||
}).catch(err => console.error('[LPSync] Background sync error:', err.message));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
111
services/arbiter-3.0/src/services/luckpermsSync.js
Normal file
111
services/arbiter-3.0/src/services/luckpermsSync.js
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* LuckPerms Meta Sync Service
|
||||
* Sets maxclaims and maxchunkloaders on all online servers when a subscriber's tier changes.
|
||||
*
|
||||
* Requires: minecraft_username linked via /link command (stored in users table)
|
||||
* Uses: Pterodactyl sendCommand API to run lp commands on each online server
|
||||
*
|
||||
* Date: April 15, 2026 (Chronicler #91, Launch Day)
|
||||
*/
|
||||
|
||||
const db = require('../database');
|
||||
const { sendCommand, getServerResources } = require('./pterodactyl');
|
||||
|
||||
// Tier level -> LuckPerms meta values
|
||||
const TIER_META = {
|
||||
1: { maxclaims: 90, maxchunkloaders: 0 }, // Awakened
|
||||
2: { maxclaims: 25, maxchunkloaders: 0 }, // Elemental
|
||||
3: { maxclaims: 25, maxchunkloaders: 0 }, // Elemental (Fire/Frost split tiers)
|
||||
4: { maxclaims: 49, maxchunkloaders: 4 }, // Knight
|
||||
5: { maxclaims: 49, maxchunkloaders: 4 }, // Knight
|
||||
6: { maxclaims: 100, maxchunkloaders: 9 }, // Master
|
||||
7: { maxclaims: 100, maxchunkloaders: 9 }, // Master
|
||||
8: { maxclaims: 121, maxchunkloaders: 16 }, // Legend
|
||||
9: { maxclaims: 121, maxchunkloaders: 16 }, // Legend
|
||||
10: { maxclaims: 225, maxchunkloaders: 81 }, // Sovereign
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all online Minecraft server identifiers from server_config
|
||||
*/
|
||||
async function getOnlineServers() {
|
||||
const result = await db.query(
|
||||
`SELECT server_identifier FROM server_config WHERE server_identifier IS NOT NULL`
|
||||
);
|
||||
const identifiers = result.rows.map(r => r.server_identifier);
|
||||
|
||||
// Filter to only running servers
|
||||
const online = [];
|
||||
await Promise.all(identifiers.map(async (id) => {
|
||||
try {
|
||||
const res = await getServerResources(id);
|
||||
if (res.state === 'running') online.push(id);
|
||||
} catch (e) {
|
||||
// Server unreachable — skip silently
|
||||
}
|
||||
}));
|
||||
|
||||
return online;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync LuckPerms meta for a subscriber across all online servers.
|
||||
* Called after Stripe checkout or tier change.
|
||||
*
|
||||
* @param {string} discordId - Discord user ID
|
||||
* @param {number} tierLevel - Subscription tier level (1-10)
|
||||
*/
|
||||
async function syncLuckPermsMeta(discordId, tierLevel) {
|
||||
const meta = TIER_META[tierLevel];
|
||||
if (!meta) {
|
||||
console.warn(`[LPSync] Unknown tier level ${tierLevel} for ${discordId} — skipping`);
|
||||
return { success: false, reason: 'unknown_tier' };
|
||||
}
|
||||
|
||||
// Look up Minecraft username
|
||||
const userResult = await db.query(
|
||||
'SELECT minecraft_username FROM users WHERE discord_id = $1',
|
||||
[discordId]
|
||||
);
|
||||
|
||||
if (userResult.rows.length === 0 || !userResult.rows[0].minecraft_username) {
|
||||
console.log(`[LPSync] No Minecraft username linked for Discord ID ${discordId} — skipping LP meta sync`);
|
||||
return { success: false, reason: 'no_minecraft_link' };
|
||||
}
|
||||
|
||||
const username = userResult.rows[0].minecraft_username;
|
||||
|
||||
// Get online servers
|
||||
const onlineServers = await getOnlineServers();
|
||||
|
||||
if (onlineServers.length === 0) {
|
||||
console.warn(`[LPSync] No online servers found — LP meta sync skipped for ${username}`);
|
||||
return { success: false, reason: 'no_online_servers' };
|
||||
}
|
||||
|
||||
// Send meta commands to all online servers
|
||||
const results = await Promise.allSettled(
|
||||
onlineServers.flatMap(serverId => [
|
||||
sendCommand(serverId, `lp user ${username} meta set maxclaims ${meta.maxclaims}`),
|
||||
sendCommand(serverId, `lp user ${username} meta set maxchunkloaders ${meta.maxchunkloaders}`)
|
||||
])
|
||||
);
|
||||
|
||||
const succeeded = results.filter(r => r.status === 'fulfilled').length;
|
||||
const failed = results.filter(r => r.status === 'rejected').length;
|
||||
|
||||
console.log(`[LPSync] Meta sync for ${username} (tier ${tierLevel}): ${succeeded} OK, ${failed} failed across ${onlineServers.length} servers`);
|
||||
console.log(`[LPSync] maxclaims=${meta.maxclaims}, maxchunkloaders=${meta.maxchunkloaders}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
username,
|
||||
tierLevel,
|
||||
meta,
|
||||
serversUpdated: onlineServers.length,
|
||||
commandsSucceeded: succeeded,
|
||||
commandsFailed: failed
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { syncLuckPermsMeta };
|
||||
Reference in New Issue
Block a user