From a27fe44b0b71acdbdcabd98edc316dedb2316aac Mon Sep 17 00:00:00 2001 From: claude-dev Date: Fri, 22 May 2026 11:12:28 +0000 Subject: [PATCH] Revert "feat(sources): X-Account-Verwaltung im Verwaltungsportal" This reverts commit bd476edb134ab71342401c45aed61a4341b4015a. --- src/routers/sources.py | 14 ++-- src/source_meta.py | 2 - src/static/dashboard.html | 84 +--------------------- src/static/js/sources.js | 142 -------------------------------------- 4 files changed, 7 insertions(+), 235 deletions(-) diff --git a/src/routers/sources.py b/src/routers/sources.py index 55377f9..458893b 100644 --- a/src/routers/sources.py +++ b/src/routers/sources.py @@ -42,7 +42,7 @@ router = APIRouter(prefix="/api/sources", tags=["sources"]) SOURCE_UPDATE_COLUMNS = { "name", "url", "domain", "source_type", "category", "status", "notes", - "language", "primary_language", "bias", "fetch_strategy", + "language", "bias", "fetch_strategy", "political_orientation", "media_type", "reliability", "state_affiliated", "country_code", } @@ -118,12 +118,11 @@ class GlobalSourceCreate(BaseModel): name: str = Field(min_length=1, max_length=200) url: Optional[str] = None domain: Optional[str] = None - source_type: str = Field(default="rss_feed", pattern="^(rss_feed|web_source|excluded|telegram_channel|podcast_feed|pdf_document|x_account)$") + source_type: str = Field(default="rss_feed", pattern="^(rss_feed|web_source|excluded|telegram_channel|podcast_feed|pdf_document)$") category: str = Field(default="sonstige") status: str = Field(default="active", pattern="^(active|inactive)$") notes: Optional[str] = None language: Optional[str] = Field(default=None, max_length=100) - primary_language: Optional[str] = Field(default=None, max_length=16) bias: Optional[str] = Field(default=None, max_length=500) fetch_strategy: Optional[str] = Field(default="default", pattern="^(default|googlebot|paywall|skip)$") @@ -132,12 +131,11 @@ class GlobalSourceUpdate(BaseModel): name: Optional[str] = Field(default=None, max_length=200) url: Optional[str] = None domain: Optional[str] = None - source_type: Optional[str] = Field(default=None, pattern="^(rss_feed|web_source|excluded|telegram_channel|podcast_feed|pdf_document|x_account)$") + source_type: Optional[str] = Field(default=None, pattern="^(rss_feed|web_source|excluded|telegram_channel|podcast_feed|pdf_document)$") category: Optional[str] = None status: Optional[str] = Field(default=None, pattern="^(active|inactive)$") notes: Optional[str] = None language: Optional[str] = Field(default=None, max_length=100) - primary_language: Optional[str] = Field(default=None, max_length=16) bias: Optional[str] = Field(default=None, max_length=500) political_orientation: Optional[str] = None media_type: Optional[str] = None @@ -232,10 +230,10 @@ async def create_global_source( ) cursor = await db.execute( - """INSERT INTO sources (name, url, domain, source_type, category, status, notes, language, primary_language, bias, fetch_strategy, added_by, tenant_id) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'system', NULL)""", + """INSERT INTO sources (name, url, domain, source_type, category, status, notes, language, bias, fetch_strategy, added_by, tenant_id) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'system', NULL)""", (data.name, data.url, data.domain, data.source_type, data.category, data.status, data.notes, - data.language, data.primary_language, data.bias, data.fetch_strategy or "default"), + data.language, data.bias, data.fetch_strategy or "default"), ) src_id = cursor.lastrowid await db.commit() diff --git a/src/source_meta.py b/src/source_meta.py index 4a9e25f..9f6faa8 100644 --- a/src/source_meta.py +++ b/src/source_meta.py @@ -38,7 +38,6 @@ SOURCE_CATEGORIES: list[CategoryEntry] = [ {"key": "russische-staatspropaganda", "label": "Russische Staatspropaganda"}, {"key": "russische-opposition", "label": "Russische Opposition / Exilmedien"}, {"key": "syrien-nahost", "label": "Syrien / Nahost"}, - {"key": "x", "label": "X-Recherche"}, ] @@ -48,7 +47,6 @@ SOURCE_TYPES: list[TypeEntry] = [ {"key": "telegram_channel", "label": "Telegram-Kanal"}, {"key": "podcast_feed", "label": "Podcast-Feed"}, {"key": "excluded", "label": "Ausgeschlossen"}, - {"key": "x_account", "label": "X-Account"}, ] diff --git a/src/static/dashboard.html b/src/static/dashboard.html index 10051c4..b4813a3 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -329,7 +329,6 @@ - @@ -472,36 +471,6 @@ - -
-
-
- - -
- -
-
-

X-Accounts (Twitter), die der Monitor als Recherchequelle nutzt. Pro Lage über die Option „X (Twitter) einbeziehen" zuschaltbar.

-
- - - - - - - - - - - - - -
AccountURLSpracheNotizArtikelStatusAktionen
-
-
-
- @@ -969,59 +938,8 @@ - - - - +
diff --git a/src/static/js/sources.js b/src/static/js/sources.js index 2b4aa12..7c51850 100644 --- a/src/static/js/sources.js +++ b/src/static/js/sources.js @@ -38,152 +38,10 @@ function setupSourceSubTabs() { else if (subtab === "tenant-sources") loadTenantSources(); else if (subtab === "source-health") loadHealthData(); else if (subtab === "classification-review") loadClassificationQueue(); - else if (subtab === "x-accounts") loadXAccounts(); }); }); } -// --- X-Accounts (Recherche-Accounts für den Monitor) --- -let xAccountsCache = []; -let editingXAccountId = null; - -function normalizeXHandle(raw) { - let h = (raw || "").trim(); - h = h.replace(/^https?:\/\//i, "").replace(/^www\./i, ""); - h = h.replace(/^(x\.com\/|twitter\.com\/|nitter\.net\/)/i, ""); - h = h.replace(/^@/, "").replace(/\/+$/, ""); - return h.split(/[/?#]/)[0]; -} - -async function loadXAccounts() { - setupXAccountForm(); - const tbody = document.getElementById("xAccountTable"); - tbody.innerHTML = 'Lade...'; - try { - const all = await API.get("/api/sources/global"); - xAccountsCache = (all || []).filter((s) => s.source_type === "x_account"); - renderXAccounts(xAccountsCache); - } catch (err) { - tbody.innerHTML = 'Fehler beim Laden'; - showToast("X-Accounts konnten nicht geladen werden", "error"); - } -} - -function renderXAccounts(list) { - const tbody = document.getElementById("xAccountTable"); - const cnt = document.getElementById("xAccountCount"); - if (cnt) cnt.textContent = list.length + (list.length === 1 ? " Account" : " Accounts"); - if (!list.length) { - tbody.innerHTML = 'Keine X-Accounts. Mit „+ X-Account hinzufügen" anlegen.'; - return; - } - tbody.innerHTML = list.map((s) => { - const handle = normalizeXHandle(s.url || s.domain || s.name || ""); - const url = "https://x.com/" + handle; - const lang = s.primary_language || s.language || "—"; - const notes = s.notes ? esc(s.notes) : ''; - const status = s.status === "active" - ? 'Aktiv' - : 'Inaktiv'; - return '' - + '' + esc(s.name || ("@" + handle)) + '' - + '' + esc(handle) + '' - + '' + esc(lang) + '' - + '' + notes + '' - + '' + (s.article_count || 0) + '' - + '' + status + '' - + '' - + ' ' - + '' - + '' - + ''; - }).join(""); -} - -function filterXAccounts() { - const q = (document.getElementById("xAccountSearch").value || "").toLowerCase(); - if (!q) { renderXAccounts(xAccountsCache); return; } - renderXAccounts(xAccountsCache.filter((s) => - (s.name || "").toLowerCase().includes(q) - || (s.url || "").toLowerCase().includes(q) - || (s.notes || "").toLowerCase().includes(q) - )); -} - -function openXAccountModal(id) { - editingXAccountId = id || null; - const errEl = document.getElementById("xAccountError"); - errEl.style.display = "none"; - const s = editingXAccountId ? xAccountsCache.find((a) => a.id === editingXAccountId) : null; - if (editingXAccountId && !s) return; - document.getElementById("xAccountModalTitle").textContent = s ? "X-Account bearbeiten" : "X-Account hinzufügen"; - document.getElementById("xAccountHandle").value = s ? normalizeXHandle(s.url || s.domain || "") : ""; - document.getElementById("xAccountName").value = s ? (s.name || "") : ""; - document.getElementById("xAccountLanguage").value = s ? (s.primary_language || s.language || "en") : "en"; - document.getElementById("xAccountNotes").value = s ? (s.notes || "") : ""; - document.getElementById("xAccountStatus").value = s ? (s.status || "active") : "active"; - openModal("modalXAccount"); -} - -function setupXAccountForm() { - const form = document.getElementById("xAccountForm"); - if (!form || form.dataset.wired) return; - form.dataset.wired = "1"; - form.addEventListener("submit", async (e) => { - e.preventDefault(); - const errEl = document.getElementById("xAccountError"); - errEl.style.display = "none"; - const handle = normalizeXHandle(document.getElementById("xAccountHandle").value); - if (!handle) { - errEl.textContent = "Bitte einen Handle oder eine x.com-URL eingeben."; - errEl.style.display = "block"; - return; - } - const nameVal = document.getElementById("xAccountName").value.trim(); - const body = { - name: nameVal || ("@" + handle), - url: "x.com/" + handle, - domain: "x.com/" + handle, - source_type: "x_account", - category: "x", - status: document.getElementById("xAccountStatus").value, - notes: document.getElementById("xAccountNotes").value.trim() || null, - primary_language: document.getElementById("xAccountLanguage").value || null, - }; - try { - if (editingXAccountId) { - await API.put("/api/sources/global/" + editingXAccountId, body); - } else { - await API.post("/api/sources/global", body); - } - closeModal("modalXAccount"); - loadXAccounts(); - showToast("X-Account gespeichert.", "success"); - } catch (err) { - errEl.textContent = err.message || "Speichern fehlgeschlagen"; - errEl.style.display = "block"; - } - }); -} - -function confirmDeleteXAccount(id) { - const s = xAccountsCache.find((a) => a.id === id); - if (!s) return; - showConfirm( - "X-Account entfernen", - 'Soll der X-Account "' + (s.name || "") + '" als Recherchequelle entfernt werden?', - async () => { - try { - await API.del("/api/sources/global/" + id); - loadXAccounts(); - showToast("X-Account entfernt.", "success"); - } catch (err) { - showToast(err.message || "Löschen fehlgeschlagen", "error"); - } - } - ); -} - // --- Grundquellen --- async function loadGlobalSources() { try {