api usage cooldown

This commit is contained in:
MDeeApp
2025-10-05 20:12:13 +02:00
parent 1b0389b63d
commit 3bcc7b08b4
4 changed files with 1266 additions and 141 deletions

View File

@@ -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, '&quot;');
}
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>