From 0ce352ad8d1eca70b67ce1913b66a75d78b805aa Mon Sep 17 00:00:00 2001 From: southseact-3d Date: Wed, 18 Feb 2026 10:40:10 +0000 Subject: [PATCH] feat(admin): Add token usage and remaining balance management options Implements both Option A and B for admin token management: - Option A (mode='remaining'): Set tokens remaining to use (calculates usage from limit) - Option B (mode='usage'): Directly set tokens consumed - Original mode='limit': Still sets token limit override The admin UI now presents 3 modes via prompt dialog, and the API endpoint handles each mode appropriately by updating the token usage bucket. --- chat/server.js | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/chat/server.js b/chat/server.js index b847308..42aa49a 100644 --- a/chat/server.js +++ b/chat/server.js @@ -16312,11 +16312,15 @@ async function handleAdminAccountTokensPost(req, res) { const body = await parseJsonBody(req); const userId = body.userId; const tokenAmount = body.tokens; + const mode = body.mode || 'limit'; // 'limit', 'usage', or 'remaining' if (!userId) return sendJson(res, 400, { error: 'User ID is required' }); if (typeof tokenAmount !== 'number' || tokenAmount < 0 || !Number.isFinite(tokenAmount)) { return sendJson(res, 400, { error: 'Invalid token amount. Must be a non-negative number.' }); } + if (!['limit', 'usage', 'remaining'].includes(mode)) { + return sendJson(res, 400, { error: 'Invalid mode. Must be "limit", "usage", or "remaining".' }); + } const user = findUserById(userId); if (!user) return sendJson(res, 404, { error: 'User not found' }); @@ -16324,16 +16328,33 @@ async function handleAdminAccountTokensPost(req, res) { const bucket = ensureTokenUsageBucket(userId); if (!bucket) return sendJson(res, 500, { error: 'Failed to access token usage data' }); - // Set the token override - bucket.tokenOverride = Math.floor(tokenAmount); - + let result = {}; + + if (mode === 'limit') { + // Set the token override (original behavior) + bucket.tokenOverride = tokenAmount === 0 ? null : Math.floor(tokenAmount); + result = { mode: 'limit', tokenOverride: bucket.tokenOverride }; + log('Admin set user token limit override', { userId, tokens: bucket.tokenOverride, admin: ADMIN_USER }); + } else if (mode === 'usage') { + // Set the usage directly (Option B) + bucket.usage = Math.floor(tokenAmount); + result = { mode: 'usage', usage: bucket.usage }; + log('Admin set user token usage', { userId, usage: bucket.usage, admin: ADMIN_USER }); + } else if (mode === 'remaining') { + // Set remaining tokens (Option A) - calculate usage from limit + const limit = getPlanTokenLimits(user.plan, userId); + const newUsage = Math.max(0, limit - Math.floor(tokenAmount)); + bucket.usage = newUsage; + result = { mode: 'remaining', remaining: Math.floor(tokenAmount), usage: newUsage, limit }; + log('Admin set user token remaining', { userId, remaining: tokenAmount, usage: newUsage, limit, admin: ADMIN_USER }); + } + await persistTokenUsage(); - + const accountData = await serializeAccount(user); - log('Admin set user token override', { userId, tokens: bucket.tokenOverride, admin: ADMIN_USER }); - sendJson(res, 200, { ok: true, account: accountData, tokenOverride: bucket.tokenOverride }); + sendJson(res, 200, { ok: true, account: accountData, ...result }); } catch (error) { - sendJson(res, 400, { error: error.message || 'Unable to update token limit' }); + sendJson(res, 400, { error: error.message || 'Unable to update token settings' }); } }