api usage cooldown
This commit is contained in:
138
web/settings.js
138
web/settings.js
@@ -123,6 +123,134 @@ async function loadSettings() {
|
||||
'Schreibe einen freundlichen, authentischen Kommentar auf Deutsch zu folgendem Facebook-Post. Der Kommentar soll natürlich wirken und maximal 2-3 Sätze lang sein:\n\n';
|
||||
}
|
||||
|
||||
function shorten(text, maxLength = 80) {
|
||||
if (typeof text !== 'string') {
|
||||
return '';
|
||||
}
|
||||
if (text.length <= maxLength) {
|
||||
return text;
|
||||
}
|
||||
return `${text.slice(0, maxLength - 3)}...`;
|
||||
}
|
||||
|
||||
function escapeHtmlAttr(text) {
|
||||
return escapeHtml(text || '').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function formatTimeLabel(iso) {
|
||||
if (!iso) return '';
|
||||
const date = new Date(iso);
|
||||
if (Number.isNaN(date.getTime())) return '';
|
||||
return date.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
|
||||
}
|
||||
|
||||
function formatRelativePast(iso) {
|
||||
if (!iso) return '';
|
||||
const date = new Date(iso);
|
||||
if (Number.isNaN(date.getTime())) return '';
|
||||
const diffMs = Date.now() - date.getTime();
|
||||
if (diffMs < 0) return 'gerade eben';
|
||||
const diffMinutes = Math.round(diffMs / 60000);
|
||||
if (diffMinutes <= 1) return 'gerade eben';
|
||||
if (diffMinutes < 60) return `vor ${diffMinutes} Min`;
|
||||
const diffHours = Math.round(diffMinutes / 60);
|
||||
if (diffHours < 24) return `vor ${diffHours} Std`;
|
||||
const diffDays = Math.round(diffHours / 24);
|
||||
if (diffDays === 1) return 'gestern';
|
||||
if (diffDays < 7) return `vor ${diffDays} Tagen`;
|
||||
return date.toLocaleDateString('de-DE');
|
||||
}
|
||||
|
||||
function formatRelativeFuture(iso) {
|
||||
if (!iso) return '';
|
||||
const date = new Date(iso);
|
||||
if (Number.isNaN(date.getTime())) return '';
|
||||
const diffMs = date.getTime() - Date.now();
|
||||
if (diffMs <= 0) return 'gleich';
|
||||
const diffMinutes = Math.round(diffMs / 60000);
|
||||
if (diffMinutes < 1) return 'gleich';
|
||||
if (diffMinutes < 60) return `in ${diffMinutes} Min`;
|
||||
const diffHours = Math.round(diffMinutes / 60);
|
||||
if (diffHours < 24) return `in ${diffHours} Std`;
|
||||
return date.toLocaleString('de-DE', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
|
||||
}
|
||||
|
||||
function buildCredentialBadges(credential) {
|
||||
const badges = [];
|
||||
if (!credential.is_active) {
|
||||
badges.push({
|
||||
label: 'Deaktiviert',
|
||||
className: 'status-badge--muted',
|
||||
title: 'Dieser Login ist derzeit deaktiviert'
|
||||
});
|
||||
} else if (credential.auto_disabled) {
|
||||
const untilText = credential.auto_disabled_until ? formatRelativeFuture(credential.auto_disabled_until) : 'läuft';
|
||||
const untilTime = credential.auto_disabled_until ? formatTimeLabel(credential.auto_disabled_until) : '';
|
||||
const reason = credential.auto_disabled_reason ? credential.auto_disabled_reason.replace(/^AUTO:/, '').trim() : 'Automatisch deaktiviert';
|
||||
badges.push({
|
||||
label: `Cooldown ${untilText}${untilTime ? ` (${untilTime})` : ''}`.trim(),
|
||||
className: 'status-badge--warning',
|
||||
title: reason || 'Automatisch deaktiviert'
|
||||
});
|
||||
} else {
|
||||
badges.push({
|
||||
label: 'Aktiv',
|
||||
className: 'status-badge--success',
|
||||
title: 'Login ist aktiv'
|
||||
});
|
||||
}
|
||||
|
||||
if (credential.last_error_message) {
|
||||
badges.push({
|
||||
label: `Fehler ${formatRelativePast(credential.last_error_at)}`.trim(),
|
||||
className: 'status-badge--danger',
|
||||
title: credential.last_error_message
|
||||
});
|
||||
}
|
||||
|
||||
if (credential.usage_24h_count) {
|
||||
const resetHint = credential.usage_24h_reset_at ? `Reset ${formatRelativeFuture(credential.usage_24h_reset_at)}` : '24h Nutzung';
|
||||
badges.push({
|
||||
label: `24h: ${credential.usage_24h_count}`,
|
||||
className: 'status-badge--info',
|
||||
title: resetHint
|
||||
});
|
||||
}
|
||||
|
||||
if (credential.last_rate_limit_remaining) {
|
||||
badges.push({
|
||||
label: `Limit: ${credential.last_rate_limit_remaining}`,
|
||||
className: 'status-badge--neutral',
|
||||
title: 'Letzter „rate limit remaining“-Wert'
|
||||
});
|
||||
}
|
||||
|
||||
return badges;
|
||||
}
|
||||
|
||||
function buildCredentialMetaLines(credential) {
|
||||
const lines = [];
|
||||
if (credential.last_success_at) {
|
||||
lines.push(`Zuletzt erfolgreich: ${formatRelativePast(credential.last_success_at)}`);
|
||||
}
|
||||
if (!credential.last_success_at && credential.last_used_at) {
|
||||
lines.push(`Zuletzt genutzt: ${formatRelativePast(credential.last_used_at)}`);
|
||||
}
|
||||
if (credential.rate_limit_reset_at && !credential.auto_disabled) {
|
||||
lines.push(`Limit-Reset ${formatRelativeFuture(credential.rate_limit_reset_at)}`);
|
||||
}
|
||||
if (credential.latest_event && credential.latest_event.type) {
|
||||
const typeLabel = credential.latest_event.type.replace(/_/g, ' ');
|
||||
const eventTime = credential.latest_event.created_at ? formatRelativePast(credential.latest_event.created_at) : '';
|
||||
const message = credential.latest_event.message ? shorten(credential.latest_event.message, 90) : '';
|
||||
const parts = [`Letztes Event (${typeLabel})`];
|
||||
if (eventTime) parts.push(eventTime);
|
||||
if (message) parts.push(`– ${message}`);
|
||||
lines.push(parts.join(' '));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
function renderCredentials() {
|
||||
const list = document.getElementById('credentialsList');
|
||||
if (!credentials.length) {
|
||||
@@ -133,6 +261,14 @@ function renderCredentials() {
|
||||
const providerName = escapeHtml(PROVIDER_INFO[c.provider]?.name || c.provider);
|
||||
const modelLabel = c.model ? ` · ${escapeHtml(c.model)}` : '';
|
||||
const endpointLabel = c.base_url ? ` · ${escapeHtml(c.base_url)}` : '';
|
||||
const badges = buildCredentialBadges(c);
|
||||
const badgesHtml = badges.length
|
||||
? `<div class="credential-item__status">${badges.map(badge => `<span class="credential-status ${badge.className}"${badge.title ? ` title="${escapeHtmlAttr(badge.title)}"` : ''}>${escapeHtml(badge.label)}</span>`).join('')}</div>`
|
||||
: '';
|
||||
const metaLines = buildCredentialMetaLines(c);
|
||||
const metaHtml = metaLines.length
|
||||
? `<div class="credential-item__meta">${metaLines.map(line => `<div>${escapeHtml(line)}</div>`).join('')}</div>`
|
||||
: '';
|
||||
|
||||
return `
|
||||
<div class="credential-item" draggable="true" data-credential-id="${c.id}" data-index="${index}">
|
||||
@@ -145,6 +281,8 @@ function renderCredentials() {
|
||||
<div>
|
||||
<div class="credential-item__name">${escapeHtml(c.name)}</div>
|
||||
<div class="credential-item__provider">${providerName}${modelLabel}${endpointLabel}</div>
|
||||
${badgesHtml}
|
||||
${metaHtml}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user