|
|
|
|
@ -7,6 +7,8 @@ let config = { hosters: {}, hosterSettings: {}, globalSettings: {} };
|
|
|
|
|
let hosterSettings = {};
|
|
|
|
|
let uploading = false;
|
|
|
|
|
let healthCheckRunning = false;
|
|
|
|
|
let accountStatuses = {}; // { 'voe.sx': { status: 'ok'|'error'|'checking'|'unchecked', message: '' } }
|
|
|
|
|
let editingAccountHoster = null; // null = adding, string = editing
|
|
|
|
|
let autoHealthCheckEnabled = true;
|
|
|
|
|
let queuePersistTimer = null;
|
|
|
|
|
const AUTO_CHECK_PREF_KEY = 'autoHealthCheckBeforeUpload';
|
|
|
|
|
@ -33,6 +35,7 @@ async function init() {
|
|
|
|
|
renderHosterSummary();
|
|
|
|
|
renderHosterModal();
|
|
|
|
|
renderSettings();
|
|
|
|
|
renderAccounts();
|
|
|
|
|
setupListeners();
|
|
|
|
|
setupDragDrop();
|
|
|
|
|
loadHistory();
|
|
|
|
|
@ -150,9 +153,10 @@ function renderHosterModal() {
|
|
|
|
|
list.innerHTML = available.map(item => {
|
|
|
|
|
const checked = selectedUploadHosters.includes(item.name);
|
|
|
|
|
const h = config.hosters[item.name] || {};
|
|
|
|
|
const subtitle = item.name === 'vidmoly.me' ? 'Login hinterlegt'
|
|
|
|
|
: (item.name === 'voe.sx' && h.username && h.password) ? 'Login hinterlegt'
|
|
|
|
|
: 'API-Key hinterlegt';
|
|
|
|
|
const st = accountStatuses[item.name];
|
|
|
|
|
const subtitle = st && st.status === 'ok' ? 'Bereit'
|
|
|
|
|
: st && st.status === 'error' ? 'Login-Fehler'
|
|
|
|
|
: getCredentialLabel(item.name, h) + ' hinterlegt';
|
|
|
|
|
return `
|
|
|
|
|
<label class="hoster-option${checked ? ' selected' : ''}" data-hoster-option="${item.name}">
|
|
|
|
|
<input type="checkbox" data-hoster-modal="${item.name}" ${checked ? 'checked' : ''}>
|
|
|
|
|
@ -897,44 +901,6 @@ function renderSettings() {
|
|
|
|
|
const panel = document.createElement('div');
|
|
|
|
|
panel.className = 'hoster-settings-panel';
|
|
|
|
|
|
|
|
|
|
let credsHtml = '';
|
|
|
|
|
if (name === 'vidmoly.me') {
|
|
|
|
|
credsHtml = `
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>Username</label>
|
|
|
|
|
<input type="text" class="key-input" data-hoster="${name}" data-field="username" value="${escapeAttr(hoster.username || '')}" placeholder="Username">
|
|
|
|
|
</div>
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>Passwort</label>
|
|
|
|
|
<input type="password" class="key-input" data-hoster="${name}" data-field="password" value="${escapeAttr(hoster.password || '')}" placeholder="Passwort">
|
|
|
|
|
<button class="toggle-vis" title="Anzeigen">👁</button>
|
|
|
|
|
</div>`;
|
|
|
|
|
} else if (name === 'voe.sx') {
|
|
|
|
|
credsHtml = `
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>E-Mail (Login)</label>
|
|
|
|
|
<input type="text" class="key-input" data-hoster="${name}" data-field="username" value="${escapeAttr(hoster.username || '')}" placeholder="E-Mail fuer Login">
|
|
|
|
|
</div>
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>Passwort (Login)</label>
|
|
|
|
|
<input type="password" class="key-input" data-hoster="${name}" data-field="password" value="${escapeAttr(hoster.password || '')}" placeholder="Passwort fuer Login">
|
|
|
|
|
<button class="toggle-vis" title="Anzeigen">👁</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>API Key (optional)</label>
|
|
|
|
|
<input type="password" class="key-input" data-hoster="${name}" data-field="apiKey" value="${escapeAttr(hoster.apiKey || '')}" placeholder="API Key (Fallback)">
|
|
|
|
|
<button class="toggle-vis" title="Anzeigen">👁</button>
|
|
|
|
|
</div>
|
|
|
|
|
<p class="hint" style="margin:4px 0 8px;opacity:0.6">Login wird bevorzugt. API-Key nur als Fallback.</p>`;
|
|
|
|
|
} else {
|
|
|
|
|
credsHtml = `
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>API Key</label>
|
|
|
|
|
<input type="password" class="key-input" data-hoster="${name}" data-field="apiKey" value="${escapeAttr(hoster.apiKey || '')}" placeholder="API Key">
|
|
|
|
|
<button class="toggle-vis" title="Anzeigen">👁</button>
|
|
|
|
|
</div>`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
panel.innerHTML = `
|
|
|
|
|
<div class="hoster-panel-header" data-hoster="${name}">
|
|
|
|
|
<span class="panel-arrow">▶</span>
|
|
|
|
|
@ -942,8 +908,6 @@ function renderSettings() {
|
|
|
|
|
<span class="panel-status ${hosterHasCredentials(name, hoster) ? 'active' : 'inactive'}">${hosterHasCredentials(name, hoster) ? 'Aktiv' : 'Inaktiv'}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="hoster-panel-body" data-panel="${name}" style="display:none">
|
|
|
|
|
${credsHtml}
|
|
|
|
|
<div class="settings-divider"></div>
|
|
|
|
|
<h4>Upload Einstellungen</h4>
|
|
|
|
|
<div class="settings-grid-mini">
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
@ -987,14 +951,6 @@ function renderSettings() {
|
|
|
|
|
body.style.display = isOpen ? 'none' : 'block';
|
|
|
|
|
arrow.innerHTML = isOpen ? '▶' : '▼';
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Toggle visibility
|
|
|
|
|
panel.querySelectorAll('.toggle-vis').forEach(btn => {
|
|
|
|
|
btn.addEventListener('click', () => {
|
|
|
|
|
const input = btn.previousElementSibling;
|
|
|
|
|
input.type = input.type === 'password' ? 'text' : 'password';
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.getElementById('chooseLogFilePathBtn')?.addEventListener('click', chooseLogFilePath);
|
|
|
|
|
@ -1008,7 +964,6 @@ async function chooseLogFilePath() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function saveSettings() {
|
|
|
|
|
const hosters = {};
|
|
|
|
|
const newHosterSettings = {};
|
|
|
|
|
const globalSettings = {
|
|
|
|
|
...(config.globalSettings || {}),
|
|
|
|
|
@ -1017,22 +972,6 @@ async function saveSettings() {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (const name of HOSTERS) {
|
|
|
|
|
// Credentials
|
|
|
|
|
if (name === 'vidmoly.me') {
|
|
|
|
|
const username = (document.querySelector(`.key-input[data-hoster="${name}"][data-field="username"]`)?.value || '').trim();
|
|
|
|
|
const password = (document.querySelector(`.key-input[data-hoster="${name}"][data-field="password"]`)?.value || '').trim();
|
|
|
|
|
hosters[name] = { enabled: !!(username && password), authType: 'login', username, password };
|
|
|
|
|
} else if (name === 'voe.sx') {
|
|
|
|
|
const username = (document.querySelector(`.key-input[data-hoster="${name}"][data-field="username"]`)?.value || '').trim();
|
|
|
|
|
const password = (document.querySelector(`.key-input[data-hoster="${name}"][data-field="password"]`)?.value || '').trim();
|
|
|
|
|
const apiKey = (document.querySelector(`.key-input[data-hoster="${name}"][data-field="apiKey"]`)?.value || '').trim();
|
|
|
|
|
hosters[name] = { enabled: !!(username && password) || !!apiKey, username, password, apiKey };
|
|
|
|
|
} else {
|
|
|
|
|
const apiKey = (document.querySelector(`.key-input[data-hoster="${name}"][data-field="apiKey"]`)?.value || '').trim();
|
|
|
|
|
hosters[name] = { enabled: !!apiKey, apiKey };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Upload settings
|
|
|
|
|
const hs = {};
|
|
|
|
|
document.querySelectorAll(`.hs-input[data-hoster="${name}"]`).forEach(input => {
|
|
|
|
|
const field = input.dataset.hs;
|
|
|
|
|
@ -1041,22 +980,303 @@ async function saveSettings() {
|
|
|
|
|
newHosterSettings[name] = hs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await window.api.saveConfig({ hosters });
|
|
|
|
|
await window.api.saveHosterSettings(newHosterSettings);
|
|
|
|
|
await window.api.saveGlobalSettings(globalSettings);
|
|
|
|
|
config = await window.api.getConfig();
|
|
|
|
|
hosterSettings = config.hosterSettings || {};
|
|
|
|
|
syncSelectedUploadHosters();
|
|
|
|
|
renderHosterSummary();
|
|
|
|
|
renderHosterModal();
|
|
|
|
|
renderSettings();
|
|
|
|
|
renderHealthCheckResults([]);
|
|
|
|
|
|
|
|
|
|
const feedback = document.getElementById('saveFeedback');
|
|
|
|
|
feedback.textContent = 'Gespeichert!';
|
|
|
|
|
setTimeout(() => { feedback.textContent = ''; }, 2000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Accounts ---
|
|
|
|
|
function getAccountsWithCreds() {
|
|
|
|
|
return HOSTERS
|
|
|
|
|
.map(name => ({ name, hoster: config.hosters[name] || {} }))
|
|
|
|
|
.filter(item => hosterHasCredentials(item.name, item.hoster));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getHostersWithoutCreds() {
|
|
|
|
|
return HOSTERS.filter(name => !hosterHasCredentials(name, config.hosters[name] || {}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getCredentialLabel(name, hoster) {
|
|
|
|
|
if (name === 'vidmoly.me') return hoster.username || 'Login';
|
|
|
|
|
if (name === 'voe.sx') return hoster.username && hoster.password ? (hoster.username || 'Login') : 'API-Key';
|
|
|
|
|
return 'API-Key';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderAccounts() {
|
|
|
|
|
const container = document.getElementById('accountsList');
|
|
|
|
|
if (!container) return;
|
|
|
|
|
|
|
|
|
|
const accounts = getAccountsWithCreds();
|
|
|
|
|
|
|
|
|
|
if (accounts.length === 0) {
|
|
|
|
|
container.innerHTML = `
|
|
|
|
|
<div class="accounts-empty">
|
|
|
|
|
<p>Keine Accounts vorhanden</p>
|
|
|
|
|
<span class="hint">Klicke auf "Account hinzufuegen" um einen Hoster einzurichten.</span>
|
|
|
|
|
</div>`;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
container.innerHTML = accounts.map(({ name, hoster }) => {
|
|
|
|
|
const st = accountStatuses[name] || { status: 'unchecked', message: '' };
|
|
|
|
|
const statusLabels = { ok: 'Bereit', checking: 'Pruefe...', error: 'Fehler', unchecked: 'Nicht geprueft' };
|
|
|
|
|
const statusLabel = statusLabels[st.status] || 'Nicht geprueft';
|
|
|
|
|
const credLabel = getCredentialLabel(name, hoster);
|
|
|
|
|
|
|
|
|
|
return `
|
|
|
|
|
<div class="account-card" data-account="${name}">
|
|
|
|
|
<div class="account-card-info">
|
|
|
|
|
<div class="account-card-title">${escapeHtml(name)}</div>
|
|
|
|
|
<div class="account-card-subtitle" title="${escapeAttr(credLabel)}">${escapeHtml(credLabel)}${st.status === 'error' && st.message ? ' — ' + escapeHtml(st.message) : ''}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<span class="account-status status-${st.status}">
|
|
|
|
|
<span class="account-status-dot"></span>
|
|
|
|
|
${statusLabel}
|
|
|
|
|
</span>
|
|
|
|
|
<div class="account-card-actions">
|
|
|
|
|
<button class="btn btn-xs btn-secondary" data-account-check="${name}">Pruefen</button>
|
|
|
|
|
<button class="btn btn-xs btn-secondary" data-account-edit="${name}">Bearbeiten</button>
|
|
|
|
|
<button class="btn btn-xs btn-danger" data-account-delete="${name}">Loeschen</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>`;
|
|
|
|
|
}).join('');
|
|
|
|
|
|
|
|
|
|
// Wire up buttons
|
|
|
|
|
container.querySelectorAll('[data-account-edit]').forEach(btn => {
|
|
|
|
|
btn.addEventListener('click', () => openAccountModal(btn.dataset.accountEdit));
|
|
|
|
|
});
|
|
|
|
|
container.querySelectorAll('[data-account-delete]').forEach(btn => {
|
|
|
|
|
btn.addEventListener('click', () => openDeleteAccountModal(btn.dataset.accountDelete));
|
|
|
|
|
});
|
|
|
|
|
container.querySelectorAll('[data-account-check]').forEach(btn => {
|
|
|
|
|
btn.addEventListener('click', () => checkSingleAccount(btn.dataset.accountCheck));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function checkSingleAccount(hosterName) {
|
|
|
|
|
accountStatuses[hosterName] = { status: 'checking', message: '' };
|
|
|
|
|
renderAccounts();
|
|
|
|
|
try {
|
|
|
|
|
const rows = await executeHealthCheck([hosterName], 'auto');
|
|
|
|
|
const row = rows.find(r => r.hoster === hosterName);
|
|
|
|
|
if (row && row.status === 'ok') {
|
|
|
|
|
accountStatuses[hosterName] = { status: 'ok', message: row.message || '' };
|
|
|
|
|
} else {
|
|
|
|
|
accountStatuses[hosterName] = { status: 'error', message: (row && row.message) || 'Pruefung fehlgeschlagen' };
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
accountStatuses[hosterName] = { status: 'error', message: err.message || 'Pruefung fehlgeschlagen' };
|
|
|
|
|
}
|
|
|
|
|
renderAccounts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getCredsFieldsHtml(name, hoster) {
|
|
|
|
|
hoster = hoster || {};
|
|
|
|
|
if (name === 'vidmoly.me') {
|
|
|
|
|
return `
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>Username</label>
|
|
|
|
|
<input type="text" class="key-input" id="accField_username" value="${escapeAttr(hoster.username || '')}" placeholder="Username">
|
|
|
|
|
</div>
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>Passwort</label>
|
|
|
|
|
<input type="password" class="key-input" id="accField_password" value="${escapeAttr(hoster.password || '')}" placeholder="Passwort">
|
|
|
|
|
<button class="toggle-vis" type="button" title="Anzeigen">👁</button>
|
|
|
|
|
</div>`;
|
|
|
|
|
}
|
|
|
|
|
if (name === 'voe.sx') {
|
|
|
|
|
return `
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>E-Mail (Login)</label>
|
|
|
|
|
<input type="text" class="key-input" id="accField_username" value="${escapeAttr(hoster.username || '')}" placeholder="E-Mail fuer Login">
|
|
|
|
|
</div>
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>Passwort (Login)</label>
|
|
|
|
|
<input type="password" class="key-input" id="accField_password" value="${escapeAttr(hoster.password || '')}" placeholder="Passwort fuer Login">
|
|
|
|
|
<button class="toggle-vis" type="button" title="Anzeigen">👁</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>API Key (optional)</label>
|
|
|
|
|
<input type="password" class="key-input" id="accField_apiKey" value="${escapeAttr(hoster.apiKey || '')}" placeholder="API Key (Fallback)">
|
|
|
|
|
<button class="toggle-vis" type="button" title="Anzeigen">👁</button>
|
|
|
|
|
</div>
|
|
|
|
|
<p class="hint" style="margin:4px 0 0;opacity:0.6">Login wird bevorzugt. API-Key nur als Fallback.</p>`;
|
|
|
|
|
}
|
|
|
|
|
// Default: API key only
|
|
|
|
|
return `
|
|
|
|
|
<div class="settings-row">
|
|
|
|
|
<label>API Key</label>
|
|
|
|
|
<input type="password" class="key-input" id="accField_apiKey" value="${escapeAttr(hoster.apiKey || '')}" placeholder="API Key">
|
|
|
|
|
<button class="toggle-vis" type="button" title="Anzeigen">👁</button>
|
|
|
|
|
</div>`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openAccountModal(editHoster) {
|
|
|
|
|
editingAccountHoster = editHoster || null;
|
|
|
|
|
const modal = document.getElementById('accountModal');
|
|
|
|
|
const title = document.getElementById('accountModalTitle');
|
|
|
|
|
const subtitle = document.getElementById('accountModalSubtitle');
|
|
|
|
|
const hosterRow = document.getElementById('accountHosterRow');
|
|
|
|
|
const hosterSelect = document.getElementById('accountHosterSelect');
|
|
|
|
|
const credsContainer = document.getElementById('accountCredsFields');
|
|
|
|
|
const statusEl = document.getElementById('accountModalStatus');
|
|
|
|
|
const saveBtn = document.getElementById('saveAccountBtn');
|
|
|
|
|
|
|
|
|
|
statusEl.textContent = '';
|
|
|
|
|
statusEl.className = 'account-modal-status';
|
|
|
|
|
|
|
|
|
|
if (editingAccountHoster) {
|
|
|
|
|
// Edit mode
|
|
|
|
|
title.textContent = 'Account bearbeiten';
|
|
|
|
|
subtitle.textContent = `Zugangsdaten fuer ${editingAccountHoster} bearbeiten.`;
|
|
|
|
|
hosterRow.style.display = 'none';
|
|
|
|
|
saveBtn.textContent = 'Speichern & Pruefen';
|
|
|
|
|
const hoster = config.hosters[editingAccountHoster] || {};
|
|
|
|
|
credsContainer.innerHTML = getCredsFieldsHtml(editingAccountHoster, hoster);
|
|
|
|
|
} else {
|
|
|
|
|
// Add mode
|
|
|
|
|
title.textContent = 'Account hinzufuegen';
|
|
|
|
|
subtitle.textContent = 'Waehle einen Hoster und gib deine Zugangsdaten ein.';
|
|
|
|
|
hosterRow.style.display = 'flex';
|
|
|
|
|
saveBtn.textContent = 'Anlegen & Pruefen';
|
|
|
|
|
const available = getHostersWithoutCreds();
|
|
|
|
|
if (available.length === 0) {
|
|
|
|
|
hosterSelect.innerHTML = '<option value="">Alle Hoster bereits eingerichtet</option>';
|
|
|
|
|
credsContainer.innerHTML = '';
|
|
|
|
|
} else {
|
|
|
|
|
hosterSelect.innerHTML = available.map(name => `<option value="${name}">${name}</option>`).join('');
|
|
|
|
|
credsContainer.innerHTML = getCredsFieldsHtml(available[0], {});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Toggle visibility buttons
|
|
|
|
|
credsContainer.querySelectorAll('.toggle-vis').forEach(btn => {
|
|
|
|
|
btn.addEventListener('click', () => {
|
|
|
|
|
const input = btn.previousElementSibling;
|
|
|
|
|
input.type = input.type === 'password' ? 'text' : 'password';
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
modal.style.display = 'flex';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeAccountModal() {
|
|
|
|
|
document.getElementById('accountModal').style.display = 'none';
|
|
|
|
|
editingAccountHoster = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openDeleteAccountModal(hosterName) {
|
|
|
|
|
const modal = document.getElementById('deleteAccountModal');
|
|
|
|
|
const msg = document.getElementById('deleteAccountMessage');
|
|
|
|
|
msg.textContent = `Account fuer "${hosterName}" wirklich loeschen? Alle Zugangsdaten werden entfernt.`;
|
|
|
|
|
modal.dataset.hoster = hosterName;
|
|
|
|
|
modal.style.display = 'flex';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeDeleteModal() {
|
|
|
|
|
document.getElementById('deleteAccountModal').style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function deleteAccount(hosterName) {
|
|
|
|
|
const hosters = { ...config.hosters };
|
|
|
|
|
// Reset credentials to defaults
|
|
|
|
|
if (hosterName === 'vidmoly.me') {
|
|
|
|
|
hosters[hosterName] = { enabled: false, authType: 'login', username: '', password: '' };
|
|
|
|
|
} else if (hosterName === 'voe.sx') {
|
|
|
|
|
hosters[hosterName] = { enabled: false, username: '', password: '', apiKey: '' };
|
|
|
|
|
} else {
|
|
|
|
|
hosters[hosterName] = { enabled: false, apiKey: '' };
|
|
|
|
|
}
|
|
|
|
|
delete accountStatuses[hosterName];
|
|
|
|
|
await window.api.saveConfig({ hosters });
|
|
|
|
|
config = await window.api.getConfig();
|
|
|
|
|
syncSelectedUploadHosters();
|
|
|
|
|
renderAccounts();
|
|
|
|
|
renderHosterSummary();
|
|
|
|
|
renderHosterModal();
|
|
|
|
|
renderSettings();
|
|
|
|
|
closeDeleteModal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function readAccountCredsFromModal(hosterName) {
|
|
|
|
|
if (hosterName === 'vidmoly.me') {
|
|
|
|
|
const username = (document.getElementById('accField_username')?.value || '').trim();
|
|
|
|
|
const password = (document.getElementById('accField_password')?.value || '').trim();
|
|
|
|
|
return { enabled: !!(username && password), authType: 'login', username, password };
|
|
|
|
|
}
|
|
|
|
|
if (hosterName === 'voe.sx') {
|
|
|
|
|
const username = (document.getElementById('accField_username')?.value || '').trim();
|
|
|
|
|
const password = (document.getElementById('accField_password')?.value || '').trim();
|
|
|
|
|
const apiKey = (document.getElementById('accField_apiKey')?.value || '').trim();
|
|
|
|
|
return { enabled: !!(username && password) || !!apiKey, username, password, apiKey };
|
|
|
|
|
}
|
|
|
|
|
const apiKey = (document.getElementById('accField_apiKey')?.value || '').trim();
|
|
|
|
|
return { enabled: !!apiKey, apiKey };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function saveAccount() {
|
|
|
|
|
const hosterName = editingAccountHoster || document.getElementById('accountHosterSelect')?.value;
|
|
|
|
|
if (!hosterName) return;
|
|
|
|
|
|
|
|
|
|
const creds = readAccountCredsFromModal(hosterName);
|
|
|
|
|
if (!creds.enabled) {
|
|
|
|
|
const statusEl = document.getElementById('accountModalStatus');
|
|
|
|
|
statusEl.textContent = 'Bitte Zugangsdaten eingeben.';
|
|
|
|
|
statusEl.className = 'account-modal-status error';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save credentials
|
|
|
|
|
const hosters = { ...config.hosters };
|
|
|
|
|
hosters[hosterName] = creds;
|
|
|
|
|
await window.api.saveConfig({ hosters });
|
|
|
|
|
config = await window.api.getConfig();
|
|
|
|
|
|
|
|
|
|
// Show checking status
|
|
|
|
|
const statusEl = document.getElementById('accountModalStatus');
|
|
|
|
|
const saveBtn = document.getElementById('saveAccountBtn');
|
|
|
|
|
statusEl.textContent = 'Pruefe Login...';
|
|
|
|
|
statusEl.className = 'account-modal-status checking';
|
|
|
|
|
saveBtn.disabled = true;
|
|
|
|
|
|
|
|
|
|
accountStatuses[hosterName] = { status: 'checking', message: '' };
|
|
|
|
|
syncSelectedUploadHosters();
|
|
|
|
|
renderAccounts();
|
|
|
|
|
renderHosterSummary();
|
|
|
|
|
renderHosterModal();
|
|
|
|
|
renderSettings();
|
|
|
|
|
|
|
|
|
|
// Run health check
|
|
|
|
|
try {
|
|
|
|
|
const rows = await executeHealthCheck([hosterName], 'auto');
|
|
|
|
|
const row = rows.find(r => r.hoster === hosterName);
|
|
|
|
|
if (row && row.status === 'ok') {
|
|
|
|
|
accountStatuses[hosterName] = { status: 'ok', message: row.message || '' };
|
|
|
|
|
statusEl.textContent = 'Login erfolgreich!';
|
|
|
|
|
statusEl.className = 'account-modal-status ok';
|
|
|
|
|
setTimeout(() => closeAccountModal(), 1200);
|
|
|
|
|
} else {
|
|
|
|
|
const msg = (row && row.message) || 'Login fehlgeschlagen';
|
|
|
|
|
accountStatuses[hosterName] = { status: 'error', message: msg };
|
|
|
|
|
statusEl.textContent = msg;
|
|
|
|
|
statusEl.className = 'account-modal-status error';
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
accountStatuses[hosterName] = { status: 'error', message: err.message || 'Pruefung fehlgeschlagen' };
|
|
|
|
|
statusEl.textContent = err.message || 'Pruefung fehlgeschlagen';
|
|
|
|
|
statusEl.className = 'account-modal-status error';
|
|
|
|
|
} finally {
|
|
|
|
|
saveBtn.disabled = false;
|
|
|
|
|
renderAccounts();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- History ---
|
|
|
|
|
async function loadHistory() {
|
|
|
|
|
const history = await window.api.getHistory();
|
|
|
|
|
@ -1250,6 +1470,41 @@ function setupListeners() {
|
|
|
|
|
document.getElementById('hosterModal').addEventListener('click', (e) => {
|
|
|
|
|
if (e.target.id === 'hosterModal') closeHosterModal();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Account management
|
|
|
|
|
document.getElementById('addAccountBtn').addEventListener('click', () => openAccountModal(null));
|
|
|
|
|
document.getElementById('closeAccountModalBtn').addEventListener('click', closeAccountModal);
|
|
|
|
|
document.getElementById('cancelAccountModalBtn').addEventListener('click', closeAccountModal);
|
|
|
|
|
document.getElementById('saveAccountBtn').addEventListener('click', saveAccount);
|
|
|
|
|
document.getElementById('accountModal').addEventListener('click', (e) => {
|
|
|
|
|
if (e.target.id === 'accountModal') closeAccountModal();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Account hoster select change → update credential fields
|
|
|
|
|
document.getElementById('accountHosterSelect').addEventListener('change', (e) => {
|
|
|
|
|
const credsContainer = document.getElementById('accountCredsFields');
|
|
|
|
|
credsContainer.innerHTML = getCredsFieldsHtml(e.target.value, {});
|
|
|
|
|
credsContainer.querySelectorAll('.toggle-vis').forEach(btn => {
|
|
|
|
|
btn.addEventListener('click', () => {
|
|
|
|
|
const input = btn.previousElementSibling;
|
|
|
|
|
input.type = input.type === 'password' ? 'text' : 'password';
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
document.getElementById('accountModalStatus').textContent = '';
|
|
|
|
|
document.getElementById('accountModalStatus').className = 'account-modal-status';
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Delete account modal
|
|
|
|
|
document.getElementById('closeDeleteModalBtn').addEventListener('click', closeDeleteModal);
|
|
|
|
|
document.getElementById('cancelDeleteBtn').addEventListener('click', closeDeleteModal);
|
|
|
|
|
document.getElementById('confirmDeleteBtn').addEventListener('click', () => {
|
|
|
|
|
const modal = document.getElementById('deleteAccountModal');
|
|
|
|
|
const hoster = modal.dataset.hoster;
|
|
|
|
|
if (hoster) deleteAccount(hoster);
|
|
|
|
|
});
|
|
|
|
|
document.getElementById('deleteAccountModal').addEventListener('click', (e) => {
|
|
|
|
|
if (e.target.id === 'deleteAccountModal') closeDeleteModal();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Update UI ---
|
|
|
|
|
|