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.
This commit is contained in:
southseact-3d
2026-02-18 10:40:10 +00:00
parent 44deabc2cf
commit 0ce352ad8d

View File

@@ -16312,11 +16312,15 @@ async function handleAdminAccountTokensPost(req, res) {
const body = await parseJsonBody(req); const body = await parseJsonBody(req);
const userId = body.userId; const userId = body.userId;
const tokenAmount = body.tokens; 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 (!userId) return sendJson(res, 400, { error: 'User ID is required' });
if (typeof tokenAmount !== 'number' || tokenAmount < 0 || !Number.isFinite(tokenAmount)) { if (typeof tokenAmount !== 'number' || tokenAmount < 0 || !Number.isFinite(tokenAmount)) {
return sendJson(res, 400, { error: 'Invalid token amount. Must be a non-negative number.' }); 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); const user = findUserById(userId);
if (!user) return sendJson(res, 404, { error: 'User not found' }); if (!user) return sendJson(res, 404, { error: 'User not found' });
@@ -16324,16 +16328,33 @@ async function handleAdminAccountTokensPost(req, res) {
const bucket = ensureTokenUsageBucket(userId); const bucket = ensureTokenUsageBucket(userId);
if (!bucket) return sendJson(res, 500, { error: 'Failed to access token usage data' }); if (!bucket) return sendJson(res, 500, { error: 'Failed to access token usage data' });
// Set the token override let result = {};
bucket.tokenOverride = Math.floor(tokenAmount);
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(); await persistTokenUsage();
const accountData = await serializeAccount(user); 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, ...result });
sendJson(res, 200, { ok: true, account: accountData, tokenOverride: bucket.tokenOverride });
} catch (error) { } catch (error) {
sendJson(res, 400, { error: error.message || 'Unable to update token limit' }); sendJson(res, 400, { error: error.message || 'Unable to update token settings' });
} }
} }