/**
* Dateiverwaltung Frontend
* Zwei getrennte Bereiche: Mail-Abruf und Datei-Sortierung
*/
// ============ API ============
async function api(endpoint, options = {}) {
const response = await fetch(`/api${endpoint}`, {
headers: { 'Content-Type': 'application/json', ...options.headers },
...options
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(error.detail || 'API Fehler');
}
return response.json();
}
// ============ Loading Overlay ============
function zeigeLoading(text = 'Wird geladen...') {
document.getElementById('loading-text').textContent = text;
document.getElementById('loading-overlay').classList.remove('hidden');
}
function versteckeLoading() {
document.getElementById('loading-overlay').classList.add('hidden');
}
// ============ File Browser ============
let browserTargetInput = null;
let browserCurrentPath = '/srv/http/dateiverwaltung/data';
function oeffneBrowser(inputId) {
browserTargetInput = inputId;
const currentValue = document.getElementById(inputId).value;
browserCurrentPath = currentValue || '/srv/http/dateiverwaltung/data';
ladeBrowserInhalt(browserCurrentPath);
document.getElementById('browser-modal').classList.remove('hidden');
}
async function ladeBrowserInhalt(path) {
try {
const data = await api(`/browse?path=${encodeURIComponent(path)}`);
if (data.error) {
document.getElementById('browser-list').innerHTML =
`
${data.error} `;
return;
}
browserCurrentPath = data.current;
document.getElementById('browser-current-path').textContent = data.current;
let html = '';
// Parent directory
if (data.parent) {
html += `
đ ..
`;
}
// Directories
for (const entry of data.entries) {
html += `
đ ${entry.name}
`;
}
if (data.entries.length === 0 && !data.parent) {
html = 'Keine Unterordner ';
}
document.getElementById('browser-list').innerHTML = html;
} catch (error) {
document.getElementById('browser-list').innerHTML =
`Fehler: ${error.message} `;
}
}
function browserSelect(element, path) {
document.querySelectorAll('.file-browser-item.selected').forEach(el => el.classList.remove('selected'));
element.classList.add('selected');
browserCurrentPath = path;
}
function browserAuswahl() {
if (browserTargetInput && browserCurrentPath) {
document.getElementById(browserTargetInput).value = browserCurrentPath + '/';
}
schliesseModal('browser-modal');
}
// ============ Checkbox Helpers ============
function getCheckedTypes(groupId) {
const checkboxes = document.querySelectorAll(`#${groupId} input[type="checkbox"]:checked`);
return Array.from(checkboxes).map(cb => cb.value);
}
function setCheckedTypes(groupId, types) {
const checkboxes = document.querySelectorAll(`#${groupId} input[type="checkbox"]`);
checkboxes.forEach(cb => {
cb.checked = types.includes(cb.value);
});
}
// ============ Init ============
document.addEventListener('DOMContentLoaded', () => {
ladePostfaecher();
ladeOrdner();
ladeRegeln();
});
// ============ BEREICH 1: Mail-Abruf ============
async function ladePostfaecher() {
try {
const postfaecher = await api('/postfaecher');
renderPostfaecher(postfaecher);
} catch (error) {
console.error('Fehler:', error);
}
}
let bearbeitetesPostfachId = null;
function renderPostfaecher(postfaecher) {
const container = document.getElementById('postfaecher-liste');
if (!postfaecher || postfaecher.length === 0) {
container.innerHTML = 'Keine PostfÀcher konfiguriert
';
return;
}
container.innerHTML = postfaecher.map(p => `
${escapeHtml(p.name)}
${escapeHtml(p.email)} â ${escapeHtml(p.ziel_ordner)}
Abrufen
Bearbeiten
Testen
Ă
`).join('');
}
function zeigePostfachModal(postfach = null) {
bearbeitetesPostfachId = postfach?.id || null;
document.getElementById('pf-name').value = postfach?.name || '';
document.getElementById('pf-server').value = postfach?.imap_server || '';
document.getElementById('pf-port').value = postfach?.imap_port || '993';
document.getElementById('pf-email').value = postfach?.email || '';
document.getElementById('pf-passwort').value = ''; // Passwort nicht vorausfĂŒllen
document.getElementById('pf-ordner').value = postfach?.ordner || 'INBOX';
document.getElementById('pf-alle-ordner').value = postfach?.alle_ordner ? 'true' : 'false';
document.getElementById('pf-ziel').value = postfach?.ziel_ordner || '/srv/http/dateiverwaltung/data/inbox/';
setCheckedTypes('pf-typen-gruppe', postfach?.erlaubte_typen || ['.pdf']);
document.getElementById('pf-max-groesse').value = postfach?.max_groesse_mb || '25';
document.getElementById('postfach-modal').classList.remove('hidden');
}
async function postfachBearbeiten(id) {
try {
const postfaecher = await api('/postfaecher');
const postfach = postfaecher.find(p => p.id === id);
if (postfach) {
zeigePostfachModal(postfach);
}
} catch (error) {
alert('Fehler: ' + error.message);
}
}
async function speicherePostfach() {
const erlaubteTypen = getCheckedTypes('pf-typen-gruppe');
if (erlaubteTypen.length === 0) {
alert('Bitte mindestens einen Dateityp auswÀhlen');
return;
}
const data = {
name: document.getElementById('pf-name').value.trim(),
imap_server: document.getElementById('pf-server').value.trim(),
imap_port: parseInt(document.getElementById('pf-port').value),
email: document.getElementById('pf-email').value.trim(),
passwort: document.getElementById('pf-passwort').value,
ordner: document.getElementById('pf-ordner').value.trim(),
alle_ordner: document.getElementById('pf-alle-ordner').value === 'true',
ziel_ordner: document.getElementById('pf-ziel').value.trim(),
erlaubte_typen: erlaubteTypen,
max_groesse_mb: parseInt(document.getElementById('pf-max-groesse').value)
};
if (!data.name || !data.imap_server || !data.email || !data.ziel_ordner) {
alert('Bitte alle Pflichtfelder ausfĂŒllen');
return;
}
// Bei Bearbeitung: Passwort nur senden wenn eingegeben
if (bearbeitetesPostfachId && !data.passwort) {
delete data.passwort;
} else if (!data.passwort) {
alert('Passwort ist erforderlich');
return;
}
try {
if (bearbeitetesPostfachId) {
await api(`/postfaecher/${bearbeitetesPostfachId}`, { method: 'PUT', body: JSON.stringify(data) });
} else {
await api('/postfaecher', { method: 'POST', body: JSON.stringify(data) });
}
schliesseModal('postfach-modal');
ladePostfaecher();
} catch (error) {
alert('Fehler: ' + error.message);
}
}
async function postfachTesten(id) {
try {
const result = await api(`/postfaecher/${id}/test`, { method: 'POST' });
alert(result.erfolg ? 'Verbindung erfolgreich!' : 'Fehler: ' + result.nachricht);
} catch (error) {
alert('Fehler: ' + error.message);
}
}
async function postfachAbrufen(id) {
const logContainer = document.getElementById('abruf-log');
logContainer.innerHTML = 'Verbinde...
';
// EventSource fĂŒr Server-Sent Events
const eventSource = new EventSource(`/api/postfaecher/${id}/abrufen/stream`);
let dateiCount = 0;
let currentOrdner = '';
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'start':
logContainer.innerHTML = `
Starte Abruf: ${escapeHtml(data.postfach)}
${data.bereits_verarbeitet} bereits verarbeitet
`;
break;
case 'info':
logContainer.innerHTML += `
${escapeHtml(data.nachricht)}
`;
break;
case 'ordner':
currentOrdner = data.name;
logContainer.innerHTML += `
đ ${escapeHtml(data.name)}
`;
break;
case 'mails':
const ordnerStatus = document.getElementById('ordner-status');
if (ordnerStatus) {
ordnerStatus.innerHTML = `đ ${escapeHtml(data.ordner)}: ${data.anzahl} Mails `;
ordnerStatus.id = ''; // ID entfernen fĂŒr nĂ€chsten Ordner
}
break;
case 'datei':
dateiCount++;
logContainer.innerHTML += `
â ${escapeHtml(data.original_name)}
${formatBytes(data.groesse)}
`;
// Scroll nach unten
logContainer.scrollTop = logContainer.scrollHeight;
break;
case 'skip':
logContainer.innerHTML += `
â ${escapeHtml(data.datei)}: ${data.grund}
`;
break;
case 'fehler':
logContainer.innerHTML += `
â ${escapeHtml(data.nachricht)}
`;
break;
case 'fertig':
logContainer.innerHTML += `
â Fertig: ${data.anzahl} Dateien gespeichert
`;
eventSource.close();
ladePostfaecher();
break;
}
};
eventSource.onerror = (error) => {
logContainer.innerHTML += `
â Verbindung unterbrochen
`;
eventSource.close();
};
}
function formatBytes(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
}
async function allePostfaecherAbrufen() {
try {
zeigeLoading('Rufe alle PostfÀcher ab...');
const result = await api('/postfaecher/abrufen-alle', { method: 'POST' });
zeigeAbrufLog(result);
ladePostfaecher();
} catch (error) {
alert('Fehler: ' + error.message);
} finally {
versteckeLoading();
}
}
async function postfachLoeschen(id) {
if (!confirm('Postfach wirklich löschen?')) return;
try {
await api(`/postfaecher/${id}`, { method: 'DELETE' });
ladePostfaecher();
} catch (error) {
alert('Fehler: ' + error.message);
}
}
function zeigeAbrufLog(result) {
const container = document.getElementById('abruf-log');
if (!result.ergebnisse || result.ergebnisse.length === 0) {
container.innerHTML = 'Keine neuen Attachments gefunden
';
return;
}
let html = '';
for (const r of result.ergebnisse) {
const status = r.fehler ? 'error' : 'success';
const icon = r.fehler ? 'â' : 'â';
html += `
${icon} ${escapeHtml(r.postfach)}: ${r.anzahl || 0} Dateien
${r.fehler ? `${escapeHtml(r.fehler)} ` : ''}
`;
if (r.dateien) {
for (const d of r.dateien) {
html += `
â ${escapeHtml(d)}
`;
}
}
}
container.innerHTML = html;
}
// ============ BEREICH 2: Datei-Sortierung ============
async function ladeOrdner() {
try {
const ordner = await api('/ordner');
renderOrdner(ordner);
} catch (error) {
console.error('Fehler:', error);
}
}
function renderOrdner(ordner) {
const container = document.getElementById('ordner-liste');
if (!ordner || ordner.length === 0) {
container.innerHTML = 'Keine Ordner konfiguriert
';
return;
}
container.innerHTML = ordner.map(o => `
${escapeHtml(o.name)} ${o.rekursiv ? 'rekursiv ' : ''}
${escapeHtml(o.pfad)} â ${escapeHtml(o.ziel_ordner)}
${(o.dateitypen || []).join(', ')}
Scannen
Ă
`).join('');
}
function zeigeOrdnerModal() {
document.getElementById('ord-name').value = '';
document.getElementById('ord-pfad').value = '/srv/http/dateiverwaltung/data/inbox/';
document.getElementById('ord-ziel').value = '/srv/http/dateiverwaltung/data/archiv/';
setCheckedTypes('ord-typen-gruppe', ['.pdf', '.jpg', '.jpeg', '.png', '.tiff']);
document.getElementById('ord-rekursiv').value = 'true';
document.getElementById('ordner-modal').classList.remove('hidden');
}
async function speichereOrdner() {
const dateitypen = getCheckedTypes('ord-typen-gruppe');
if (dateitypen.length === 0) {
alert('Bitte mindestens einen Dateityp auswÀhlen');
return;
}
const data = {
name: document.getElementById('ord-name').value.trim(),
pfad: document.getElementById('ord-pfad').value.trim(),
ziel_ordner: document.getElementById('ord-ziel').value.trim(),
rekursiv: document.getElementById('ord-rekursiv').value === 'true',
dateitypen: dateitypen
};
if (!data.name || !data.pfad || !data.ziel_ordner) {
alert('Bitte alle Felder ausfĂŒllen');
return;
}
try {
zeigeLoading('Speichere Ordner...');
await api('/ordner', { method: 'POST', body: JSON.stringify(data) });
schliesseModal('ordner-modal');
ladeOrdner();
} catch (error) {
alert('Fehler: ' + error.message);
} finally {
versteckeLoading();
}
}
async function ordnerLoeschen(id) {
if (!confirm('Ordner wirklich löschen?')) return;
try {
await api(`/ordner/${id}`, { method: 'DELETE' });
ladeOrdner();
} catch (error) {
alert('Fehler: ' + error.message);
}
}
async function ordnerScannen(id) {
try {
const result = await api(`/ordner/${id}/scannen`);
alert(`${result.anzahl} Dateien im Ordner gefunden`);
} catch (error) {
alert('Fehler: ' + error.message);
}
}
// ============ Regeln ============
let editierteRegelId = null;
async function ladeRegeln() {
try {
const regeln = await api('/regeln');
renderRegeln(regeln);
} catch (error) {
console.error('Fehler:', error);
}
}
function renderRegeln(regeln) {
const container = document.getElementById('regeln-liste');
if (!regeln || regeln.length === 0) {
container.innerHTML = 'Keine Regeln definiert
';
return;
}
container.innerHTML = regeln.map(r => `
${escapeHtml(r.name)} Prio ${r.prioritaet}
${escapeHtml(r.schema)}
Bearbeiten
Ă
`).join('');
}
function zeigeRegelModal(regel = null) {
editierteRegelId = regel?.id || null;
document.getElementById('regel-modal-title').textContent = regel ? 'Regel bearbeiten' : 'Regel hinzufĂŒgen';
document.getElementById('regel-name').value = regel?.name || '';
document.getElementById('regel-prioritaet').value = regel?.prioritaet || 100;
document.getElementById('regel-muster').value = JSON.stringify(regel?.muster || {"text_match_any": [], "text_match": []}, null, 2);
document.getElementById('regel-extraktion').value = JSON.stringify(regel?.extraktion || {}, null, 2);
document.getElementById('regel-schema').value = regel?.schema || '{datum} - Dokument.pdf';
document.getElementById('regel-unterordner').value = regel?.unterordner || '';
document.getElementById('regel-test-text').value = '';
document.getElementById('regel-test-ergebnis').classList.add('hidden');
document.getElementById('regel-modal').classList.remove('hidden');
}
async function bearbeiteRegel(id) {
try {
const regeln = await api('/regeln');
const regel = regeln.find(r => r.id === id);
if (regel) zeigeRegelModal(regel);
} catch (error) {
alert('Fehler: ' + error.message);
}
}
async function speichereRegel() {
let muster, extraktion;
try {
muster = JSON.parse(document.getElementById('regel-muster').value);
} catch (e) {
alert('UngĂŒltiges JSON im Muster-Feld');
return;
}
try {
extraktion = JSON.parse(document.getElementById('regel-extraktion').value);
} catch (e) {
alert('UngĂŒltiges JSON im Extraktion-Feld');
return;
}
const data = {
name: document.getElementById('regel-name').value.trim(),
prioritaet: parseInt(document.getElementById('regel-prioritaet').value),
muster,
extraktion,
schema: document.getElementById('regel-schema').value.trim(),
unterordner: document.getElementById('regel-unterordner').value.trim() || null
};
if (!data.name) {
alert('Bitte einen Namen eingeben');
return;
}
try {
if (editierteRegelId) {
await api(`/regeln/${editierteRegelId}`, { method: 'PUT', body: JSON.stringify(data) });
} else {
await api('/regeln', { method: 'POST', body: JSON.stringify(data) });
}
schliesseModal('regel-modal');
ladeRegeln();
} catch (error) {
alert('Fehler: ' + error.message);
}
}
async function regelLoeschen(id) {
if (!confirm('Regel wirklich löschen?')) return;
try {
await api(`/regeln/${id}`, { method: 'DELETE' });
ladeRegeln();
} catch (error) {
alert('Fehler: ' + error.message);
}
}
async function testeRegel() {
const text = document.getElementById('regel-test-text').value;
if (!text) {
alert('Bitte Testtext eingeben');
return;
}
let muster, extraktion;
try {
muster = JSON.parse(document.getElementById('regel-muster').value);
extraktion = JSON.parse(document.getElementById('regel-extraktion').value);
} catch (e) {
alert('UngĂŒltiges JSON');
return;
}
const regel = {
name: 'Test',
muster,
extraktion,
schema: document.getElementById('regel-schema').value.trim()
};
try {
const result = await api('/regeln/test', {
method: 'POST',
body: JSON.stringify({ regel, text })
});
const container = document.getElementById('regel-test-ergebnis');
container.classList.remove('hidden', 'success', 'error');
if (result.passt) {
container.classList.add('success');
container.textContent = `â Regel passt!\n\nExtrahiert:\n${JSON.stringify(result.extrahiert, null, 2)}\n\nDateiname:\n${result.dateiname}`;
} else {
container.classList.add('error');
container.textContent = 'â Regel passt nicht';
}
} catch (error) {
alert('Fehler: ' + error.message);
}
}
// ============ Sortierung starten ============
async function sortierungStarten() {
try {
zeigeLoading('Sortiere Dateien...');
const result = await api('/sortierung/starten', { method: 'POST' });
zeigeSortierungLog(result);
} catch (error) {
alert('Fehler: ' + error.message);
} finally {
versteckeLoading();
}
}
function zeigeSortierungLog(result) {
const container = document.getElementById('sortierung-log');
if (!result.verarbeitet || result.verarbeitet.length === 0) {
container.innerHTML = 'Keine Dateien verarbeitet
';
return;
}
let html = `
Gesamt: ${result.gesamt} | Sortiert: ${result.sortiert} | ZUGFeRD: ${result.zugferd} | Fehler: ${result.fehler}
`;
for (const d of result.verarbeitet) {
const status = d.fehler ? 'error' : (d.zugferd ? 'info' : 'success');
const icon = d.fehler ? 'â' : (d.zugferd ? 'đ§Ÿ' : 'â');
html += `
${icon} ${escapeHtml(d.neuer_name || d.original)}
${d.fehler ? `${escapeHtml(d.fehler)} ` : ''}
`;
}
container.innerHTML = html;
}
// ============ Utilities ============
function schliesseModal(id) {
document.getElementById(id).classList.add('hidden');
}
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
document.addEventListener('click', (e) => {
if (e.target.classList.contains('modal')) {
e.target.classList.add('hidden');
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
document.querySelectorAll('.modal:not(.hidden)').forEach(m => m.classList.add('hidden'));
}
});