Display OpenRouter credit balance in UI (#5513)
* Display OpenRouter credit balance in UI Adds a "View Remaining Credits" click handler that fetches the current balance from the OpenRouter /credits endpoint via a new server-side /api/openrouter/credits route, and renders it next to the link. The anchor still points at openrouter.ai/account so middle-click / right-click "open in new tab" keeps working. * Return 500 on OpenRouter credits failure * Reduce to two decimals * Update view credits URL --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
This commit is contained in:
+4
-2
@@ -2446,7 +2446,8 @@
|
||||
<small data-i18n="Click Authorize below or get the key from">
|
||||
Click "Authorize" below or get the key from </small> <a target="_blank" href="https://openrouter.ai/keys/">OpenRouter</a>.
|
||||
<br>
|
||||
<a href="https://openrouter.ai/account" target="_blank" data-i18n="View Remaining Credits">View Remaining Credits</a>
|
||||
<a href="https://openrouter.ai/settings/credits" target="_blank" class="openrouter_view_credits" data-i18n="View Remaining Credits">View Remaining Credits</a>
|
||||
<span class="openrouter_credits_display marginLeft5"></span>
|
||||
</div>
|
||||
<div class="flex-container">
|
||||
<input id="api_key_openrouter-tg" name="api_key_openrouter" class="text_pole flex1 api_key_openrouter" value="" type="text" autocomplete="off">
|
||||
@@ -3179,7 +3180,8 @@
|
||||
<small data-i18n="Click Authorize below or get the key from">
|
||||
Click "Authorize" below or get the key from </small> <a target="_blank" href="https://openrouter.ai/keys/">OpenRouter</a>.
|
||||
<br>
|
||||
<a href="https://openrouter.ai/account" target="_blank" data-i18n="View Remaining Credits">View Remaining Credits</a>
|
||||
<a href="https://openrouter.ai/settings/credits" target="_blank" class="openrouter_view_credits" data-i18n="View Remaining Credits">View Remaining Credits</a>
|
||||
<span class="openrouter_credits_display marginLeft5"></span>
|
||||
</div>
|
||||
<div class="flex-container">
|
||||
<input id="api_key_openrouter" name="api_key_openrouter" class="text_pole flex1 api_key_openrouter" value="" type="text" autocomplete="off">
|
||||
|
||||
@@ -1120,5 +1120,28 @@ export async function initSecrets() {
|
||||
warningElement.toggle(value.length > 0);
|
||||
});
|
||||
$('.openrouter_authorize').on('click', authorizeOpenRouter);
|
||||
$(document).on('click', '.openrouter_view_credits', async function (event) {
|
||||
event.preventDefault();
|
||||
const display = $(this).siblings('.openrouter_credits_display').first();
|
||||
display.text(t`Loading…`);
|
||||
try {
|
||||
const response = await fetch('/api/openrouter/credits', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
if (typeof data.remaining !== 'number') {
|
||||
throw new Error('Invalid response');
|
||||
}
|
||||
display.text(`$${data.remaining.toFixed(2)}`);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch OpenRouter credits:', error);
|
||||
display.text('');
|
||||
toastr.error(t`Could not fetch OpenRouter credits. Please try again.`);
|
||||
}
|
||||
});
|
||||
registerSecretSlashCommands();
|
||||
}
|
||||
|
||||
@@ -100,6 +100,41 @@ router.post('/models/image', async (_req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/credits', async (req, res) => {
|
||||
try {
|
||||
const key = readSecret(req.user.directories, SECRET_KEYS.OPENROUTER);
|
||||
|
||||
if (!key) {
|
||||
console.warn('OpenRouter API key not found');
|
||||
return res.sendStatus(400);
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_OPENROUTER}/credits`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${key}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn('OpenRouter credits request failed', response.statusText);
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
|
||||
/** @type {any} */
|
||||
const data = await response.json();
|
||||
const totalCredits = data.data?.total_credits ?? 0;
|
||||
const totalUsage = data.data?.total_usage ?? 0;
|
||||
const remaining = totalCredits - totalUsage;
|
||||
|
||||
return res.json({ remaining, total_credits: totalCredits, total_usage: totalUsage });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/image/generate', async (req, res) => {
|
||||
try {
|
||||
const key = readSecret(req.user.directories, SECRET_KEYS.OPENROUTER);
|
||||
|
||||
Reference in New Issue
Block a user