feat(sources): PDF-Upload auch in der Endkunden-App (Kundenquelle)

- POST /api/sources/upload-pdf: tenant-scoped Upload, gleiche Speicher-
  Konvention wie der Verwaltungs-Endpoint (<dirname(DB)>/pdfs/{sha}.pdf).
  Duplikat-Check beruecksichtigt globale Quellen.
- dashboard.html: +PDF-Button in der Quellenverwaltungs-Toolbar +
  eigenes Modal modal-pdf-upload (closeModal-Quotes via &#39;).
- app.js: App.openPdfUpload + _bindPdfUploadFormOnce (Submit nur einmal
  binden).
- api.js: API.upload(path, formData) Helper analog Verwaltung.
Dieser Commit ist enthalten in:
Claude Code
2026-05-16 23:57:32 +00:00
Ursprung e68386f6bb
Commit 168fbc3987
4 geänderte Dateien mit 255 neuen und 1 gelöschten Zeilen

Datei anzeigen

@@ -22,6 +22,31 @@ const API = {
};
},
async upload(path, formData) {
const token = localStorage.getItem("osint_token");
const headers = {};
if (token) headers["Authorization"] = `Bearer ${token}`;
const response = await fetch(`${this.baseUrl}${path}`, {
method: "POST",
headers,
body: formData,
});
if (response.status === 401) {
localStorage.removeItem("osint_token");
localStorage.removeItem("osint_username");
window.location.href = "/";
return;
}
if (!response.ok) {
const data = await response.json().catch(() => ({}));
let d = data.detail;
if (Array.isArray(d)) d = d.map(e => e.msg || JSON.stringify(e)).join("; ");
else if (typeof d === "object" && d !== null) d = JSON.stringify(d);
throw new Error(d || `Fehler ${response.status}`);
}
return response.json();
},
async _request(method, path, body = null, externalSignal = null) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30000);