364 lines
15 KiB
HTML
364 lines
15 KiB
HTML
{% extends "base.html" %}
|
|
{% set active_page = "platforms" %}
|
|
|
|
{% block content %}
|
|
|
|
{% if message %}
|
|
<div class="alert alert-{{ message_type or 'info' }}">{{ message }}</div>
|
|
{% endif %}
|
|
|
|
<div class="card">
|
|
<h2>Amazon Business - Einstellungen</h2>
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="amazon_enabled">Status</label>
|
|
<select id="amazon_enabled" name="amazon_enabled">
|
|
<option value="false" {% if settings.get('amazon_enabled') != 'true' %}selected{% endif %}>Deaktiviert</option>
|
|
<option value="true" {% if settings.get('amazon_enabled') == 'true' %}selected{% endif %}>Aktiviert</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="amazon_domain">Amazon-Domain</label>
|
|
<select id="amazon_domain" name="amazon_domain">
|
|
<option value="amazon.de" {% if settings.get('amazon_domain') == 'amazon.de' %}selected{% endif %}>amazon.de</option>
|
|
<option value="amazon.at" {% if settings.get('amazon_domain') == 'amazon.at' %}selected{% endif %}>amazon.at</option>
|
|
<option value="amazon.com" {% if settings.get('amazon_domain') == 'amazon.com' %}selected{% endif %}>amazon.com</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="amazon_email">Amazon E-Mail</label>
|
|
<input type="email" id="amazon_email" name="amazon_email"
|
|
value="{{ settings.get('amazon_email', '') }}"
|
|
placeholder="email@example.com">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="amazon_password">Amazon Passwort</label>
|
|
<input type="password" id="amazon_password" name="amazon_password"
|
|
placeholder="{% if settings.get('amazon_password') %}(gespeichert){% else %}Passwort eingeben{% endif %}">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="amazon_since_date">Rechnungen ab Datum</label>
|
|
<input type="date" id="amazon_since_date" name="amazon_since_date"
|
|
value="{{ settings.get('amazon_since_date', '') }}">
|
|
<small class="text-muted">Leer = letzte 30 Tage</small>
|
|
</div>
|
|
<div class="form-group" style="align-self:end;">
|
|
{% if settings.get('amazon_last_sync') %}
|
|
<small class="text-muted">Letzter Abruf: {{ settings.get('amazon_last_sync') }}</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="form-actions" style="margin-top:1rem;">
|
|
<button type="button" class="btn btn-primary" onclick="saveAmazonSettings()">Einstellungen speichern</button>
|
|
</div>
|
|
<div id="settingsMsg" style="margin-top:0.75rem;"></div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Anmeldung & Abruf</h2>
|
|
<div style="display:flex;align-items:center;gap:0.75rem;margin-bottom:1rem;">
|
|
<span>Session:</span>
|
|
<span id="sessionBadge" class="badge badge-inactive">Wird geprüft...</span>
|
|
</div>
|
|
|
|
<div style="display:flex;gap:0.75rem;flex-wrap:wrap;">
|
|
<button type="button" id="btnLogin" class="btn btn-primary" onclick="doLogin()">Bei Amazon anmelden</button>
|
|
<button type="button" id="btnLogout" class="btn btn-secondary" onclick="doLogout()" style="display:none;">Session löschen</button>
|
|
<button type="button" id="btnProcess" class="btn btn-success" onclick="doProcess()" style="display:none;">Jetzt Rechnungen abrufen</button>
|
|
<button type="button" class="btn btn-secondary" onclick="doReset()">Importierte zurücksetzen</button>
|
|
</div>
|
|
<div id="processMsg" style="margin-top:0.75rem;"></div>
|
|
</div>
|
|
|
|
<!-- Interactive Browser Modal -->
|
|
<div id="browserModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:1000;overflow:auto;">
|
|
<div style="max-width:1340px;margin:2rem auto;background:var(--card-bg);border-radius:8px;padding:1.5rem;position:relative;">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
|
|
<h3 style="margin:0;">Amazon Login - Interaktiver Browser</h3>
|
|
<button type="button" class="btn btn-secondary" onclick="closeBrowser()" style="padding:0.25rem 0.75rem;">Schließen</button>
|
|
</div>
|
|
<div id="browserInfo" class="alert alert-info" style="margin-bottom:1rem;">
|
|
Klicken und tippen Sie im Browser-Bild, um sich bei Amazon anzumelden. CAPTCHAs und 2FA können Sie direkt lösen.
|
|
</div>
|
|
<div style="position:relative;display:inline-block;border:1px solid var(--border-color);cursor:crosshair;">
|
|
<img id="browserImg" src="" alt="Browser" style="display:block;max-width:100%;height:auto;"
|
|
onclick="onBrowserClick(event)">
|
|
</div>
|
|
<div style="margin-top:1rem;display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap;">
|
|
<input type="text" id="browserInput" placeholder="Text eingeben und Enter drücken..."
|
|
style="flex:1;min-width:200px;padding:0.5rem;border:1px solid var(--border-color);border-radius:4px;background:var(--input-bg);color:var(--text-color);"
|
|
onkeydown="onBrowserKeydown(event)">
|
|
<button type="button" class="btn btn-primary" onclick="sendBrowserText()">Senden</button>
|
|
<button type="button" class="btn btn-secondary" onclick="sendKey('Tab')">Tab</button>
|
|
<button type="button" class="btn btn-secondary" onclick="sendKey('Escape')">Esc</button>
|
|
<button type="button" class="btn btn-secondary" onclick="sendKey('Backspace')">⌫</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// --- Settings ---
|
|
async function saveAmazonSettings() {
|
|
const btn = event.target;
|
|
btn.disabled = true;
|
|
btn.textContent = 'Speichert...';
|
|
const msgEl = document.getElementById('settingsMsg');
|
|
|
|
const data = {
|
|
amazon_enabled: document.getElementById('amazon_enabled').value,
|
|
amazon_domain: document.getElementById('amazon_domain').value,
|
|
amazon_email: document.getElementById('amazon_email').value,
|
|
amazon_password: document.getElementById('amazon_password').value,
|
|
amazon_since_date: document.getElementById('amazon_since_date').value,
|
|
};
|
|
|
|
try {
|
|
const resp = await fetch('/api/amazon-settings', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify(data),
|
|
});
|
|
const result = await resp.json();
|
|
if (result.success) {
|
|
msgEl.innerHTML = '<div class="alert alert-success">Einstellungen gespeichert</div>';
|
|
if (data.amazon_password) {
|
|
document.getElementById('amazon_password').value = '';
|
|
document.getElementById('amazon_password').placeholder = '(gespeichert)';
|
|
}
|
|
} else {
|
|
msgEl.innerHTML = '<div class="alert alert-error">' + escapeHtml(result.error || 'Fehler') + '</div>';
|
|
}
|
|
} catch (e) {
|
|
msgEl.innerHTML = '<div class="alert alert-error">Verbindungsfehler</div>';
|
|
} finally {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Einstellungen speichern';
|
|
}
|
|
}
|
|
|
|
// --- Session Status ---
|
|
async function checkSession() {
|
|
const badge = document.getElementById('sessionBadge');
|
|
try {
|
|
const resp = await fetch('/api/amazon-status');
|
|
const data = await resp.json();
|
|
if (data.login_active) {
|
|
badge.className = 'badge badge-warning';
|
|
badge.textContent = 'Login läuft...';
|
|
document.getElementById('btnLogout').style.display = 'none';
|
|
document.getElementById('btnProcess').style.display = 'none';
|
|
} else if (data.session_valid) {
|
|
badge.className = 'badge badge-success';
|
|
badge.textContent = 'Angemeldet';
|
|
document.getElementById('btnLogout').style.display = '';
|
|
document.getElementById('btnProcess').style.display = '';
|
|
} else {
|
|
badge.className = 'badge badge-inactive';
|
|
badge.textContent = 'Nicht angemeldet';
|
|
document.getElementById('btnLogout').style.display = 'none';
|
|
document.getElementById('btnProcess').style.display = 'none';
|
|
}
|
|
} catch (e) {
|
|
badge.className = 'badge badge-inactive';
|
|
badge.textContent = 'Unbekannt';
|
|
}
|
|
}
|
|
|
|
// --- Interactive Browser Login ---
|
|
let screenshotInterval = null;
|
|
let loginPollInterval = null;
|
|
|
|
async function doLogin() {
|
|
const msgEl = document.getElementById('processMsg');
|
|
msgEl.innerHTML = '<div class="alert alert-info">Browser wird gestartet...</div>';
|
|
|
|
try {
|
|
await fetch('/api/amazon-login', {method: 'POST'});
|
|
// Open browser modal
|
|
document.getElementById('browserModal').style.display = '';
|
|
msgEl.innerHTML = '';
|
|
startScreenshotPolling();
|
|
startLoginStatePolling();
|
|
} catch (e) {
|
|
msgEl.innerHTML = '<div class="alert alert-error">Browser konnte nicht gestartet werden</div>';
|
|
}
|
|
}
|
|
|
|
function startScreenshotPolling() {
|
|
if (screenshotInterval) clearInterval(screenshotInterval);
|
|
refreshScreenshot();
|
|
screenshotInterval = setInterval(refreshScreenshot, 1500);
|
|
}
|
|
|
|
async function refreshScreenshot() {
|
|
try {
|
|
const resp = await fetch('/api/amazon-browser-screenshot');
|
|
if (resp.ok) {
|
|
const blob = await resp.blob();
|
|
document.getElementById('browserImg').src = URL.createObjectURL(blob);
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
function startLoginStatePolling() {
|
|
if (loginPollInterval) clearInterval(loginPollInterval);
|
|
loginPollInterval = setInterval(async () => {
|
|
try {
|
|
const resp = await fetch('/api/amazon-login-state');
|
|
const data = await resp.json();
|
|
const info = document.getElementById('browserInfo');
|
|
|
|
if (data.status === 'logged_in') {
|
|
info.className = 'alert alert-success';
|
|
info.textContent = 'Erfolgreich angemeldet! Sie können das Fenster schließen.';
|
|
} else if (data.status === 'login_failed') {
|
|
info.className = 'alert alert-error';
|
|
info.textContent = data.message || 'Login fehlgeschlagen';
|
|
} else if (data.status === 'interactive') {
|
|
info.className = 'alert alert-info';
|
|
info.textContent = data.message || 'Bitte im Browser anmelden...';
|
|
}
|
|
} catch (e) {}
|
|
}, 2000);
|
|
}
|
|
|
|
async function onBrowserClick(event) {
|
|
const img = event.target;
|
|
const rect = img.getBoundingClientRect();
|
|
// Scale click coordinates to actual browser viewport (1280x800)
|
|
const scaleX = 1280 / img.clientWidth;
|
|
const scaleY = 800 / img.clientHeight;
|
|
const x = Math.round((event.clientX - rect.left) * scaleX);
|
|
const y = Math.round((event.clientY - rect.top) * scaleY);
|
|
|
|
try {
|
|
await fetch('/api/amazon-browser-click', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({x, y}),
|
|
});
|
|
setTimeout(refreshScreenshot, 500);
|
|
} catch (e) {}
|
|
}
|
|
|
|
async function sendBrowserText() {
|
|
const input = document.getElementById('browserInput');
|
|
const text = input.value;
|
|
if (!text) return;
|
|
|
|
try {
|
|
await fetch('/api/amazon-browser-type', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({text}),
|
|
});
|
|
input.value = '';
|
|
setTimeout(refreshScreenshot, 500);
|
|
} catch (e) {}
|
|
}
|
|
|
|
async function sendKey(key) {
|
|
try {
|
|
await fetch('/api/amazon-browser-key', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({key}),
|
|
});
|
|
setTimeout(refreshScreenshot, 500);
|
|
} catch (e) {}
|
|
}
|
|
|
|
function onBrowserKeydown(event) {
|
|
if (event.key === 'Enter') {
|
|
event.preventDefault();
|
|
sendBrowserText();
|
|
// Also send Enter to the browser
|
|
sendKey('Enter');
|
|
}
|
|
}
|
|
|
|
async function closeBrowser() {
|
|
if (screenshotInterval) { clearInterval(screenshotInterval); screenshotInterval = null; }
|
|
if (loginPollInterval) { clearInterval(loginPollInterval); loginPollInterval = null; }
|
|
document.getElementById('browserModal').style.display = 'none';
|
|
try {
|
|
await fetch('/api/amazon-login-close', {method: 'POST'});
|
|
} catch (e) {}
|
|
checkSession();
|
|
}
|
|
|
|
// --- Logout ---
|
|
async function doLogout() {
|
|
if (!confirm('Amazon-Session wirklich löschen? Sie müssen sich danach neu anmelden.')) return;
|
|
const btn = document.getElementById('btnLogout');
|
|
btn.disabled = true;
|
|
try {
|
|
await fetch('/api/amazon-logout', {method: 'POST'});
|
|
document.getElementById('processMsg').innerHTML = '<div class="alert alert-info">Session gelöscht</div>';
|
|
} catch (e) {
|
|
document.getElementById('processMsg').innerHTML = '<div class="alert alert-error">Fehler</div>';
|
|
} finally {
|
|
btn.disabled = false;
|
|
checkSession();
|
|
}
|
|
}
|
|
|
|
// --- Process ---
|
|
async function doProcess() {
|
|
const btn = document.getElementById('btnProcess');
|
|
const msgEl = document.getElementById('processMsg');
|
|
btn.disabled = true;
|
|
btn.textContent = 'Rechnungen werden abgerufen...';
|
|
msgEl.innerHTML = '';
|
|
|
|
try {
|
|
const resp = await fetch('/api/amazon-process', {method: 'POST'});
|
|
const data = await resp.json();
|
|
if (data.error) {
|
|
msgEl.innerHTML = '<div class="alert alert-error">' + escapeHtml(data.error) + '</div>';
|
|
} else if (data.processed > 0 || data.errors > 0) {
|
|
let msg = data.processed + ' Rechnung(en) importiert';
|
|
if (data.skipped > 0) msg += ', ' + data.skipped + ' übersprungen';
|
|
if (data.errors > 0) msg += ', ' + data.errors + ' Fehler';
|
|
const cls = data.errors > 0 ? 'warning' : 'success';
|
|
msgEl.innerHTML = '<div class="alert alert-' + cls + '">' + escapeHtml(msg) + '</div>';
|
|
} else {
|
|
msgEl.innerHTML = '<div class="alert alert-info">Keine neuen Rechnungen gefunden</div>';
|
|
}
|
|
} catch (e) {
|
|
msgEl.innerHTML = '<div class="alert alert-error">Verbindungsfehler</div>';
|
|
} finally {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Jetzt Rechnungen abrufen';
|
|
checkSession();
|
|
}
|
|
}
|
|
|
|
// --- Reset ---
|
|
async function doReset() {
|
|
if (!confirm('Alle als importiert markierten Rechnungen zurücksetzen? Beim nächsten Abruf werden alle Rechnungen erneut heruntergeladen und gesendet.')) return;
|
|
const msgEl = document.getElementById('processMsg');
|
|
try {
|
|
const resp = await fetch('/api/amazon-reset', {method: 'POST'});
|
|
const data = await resp.json();
|
|
if (data.success) {
|
|
msgEl.innerHTML = '<div class="alert alert-info">' + data.count + ' Bestellung(en) zurückgesetzt</div>';
|
|
} else {
|
|
msgEl.innerHTML = '<div class="alert alert-error">' + escapeHtml(data.error || 'Fehler') + '</div>';
|
|
}
|
|
} catch (e) {
|
|
msgEl.innerHTML = '<div class="alert alert-error">Verbindungsfehler</div>';
|
|
}
|
|
}
|
|
|
|
function escapeHtml(str) {
|
|
const d = document.createElement('div');
|
|
d.textContent = str;
|
|
return d.innerHTML;
|
|
}
|
|
|
|
// Initial check
|
|
checkSession();
|
|
</script>
|
|
{% endblock %}
|