Scanner: Jooble entfernt

Quelle, Schlüsselfeld in Einstellungen -> Scanner und Doku raus.
Agenten, die jooble noch gespeichert haben, bekommen es nicht mehr
angezeigt; die Pipeline überspringt unbekannte Quellen ohnehin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-29 11:24:05 +02:00
co-authored by Claude Opus 5.5
parent a85daf01da
commit e0f6ef06cd
9 changed files with 20 additions and 62 deletions
+3 -2
View File
@@ -31,7 +31,8 @@ const agentOut = (row) => ({
id: row.id,
name: row.name,
keywords: parseList(row.keywords),
sources: parseList(row.sources),
// Nur Quellen, die es noch gibt (Jooble ist am 29.09.2026 entfallen).
sources: parseList(row.sources).filter((id) => SOURCE_IDS.includes(id)),
excludes: parseList(row.excludes),
is_active: Boolean(row.is_active),
matches: Number(row.matches ?? 0),
@@ -323,7 +324,7 @@ router.get('/settings', requireAdmin, async (req, res, next) => {
router.patch('/settings', requireAdmin, async (req, res, next) => {
try {
const settings = await saveScannerSettings(req.body ?? {});
const touchedKeys = ['adzunaId', 'adzunaKey', 'joobleKey'].filter((key) => req.body?.[key] !== undefined);
const touchedKeys = ['adzunaId', 'adzunaKey'].filter((key) => req.body?.[key] !== undefined);
await logHistory({
entityType: 'system',
entityLabel: 'Scanner',
-1
View File
@@ -22,7 +22,6 @@ export const DEFAULTS = {
const SECRET_KEYS = {
adzunaId: 'scanner_adzuna_id',
adzunaKey: 'scanner_adzuna_key',
joobleKey: 'scanner_jooble_key',
};
const TIME = /^([01]\d|2[0-3]):[0-5]\d$/;
-34
View File
@@ -26,7 +26,6 @@ export const SOURCES = {
stepstone: { label: 'StepStone', countries: 'DE', needsKey: null, fetch: fetchStepstone },
karriere: { label: 'karriere.at', countries: 'AT', needsKey: null, fetch: fetchKarriere },
adzuna: { label: 'Adzuna', countries: 'DE · AT · CH', needsKey: 'adzuna', fetch: fetchAdzuna },
jooble: { label: 'Jooble', countries: 'DE · AT · CH', needsKey: 'jooble', fetch: fetchJooble },
freelancermap: { label: 'freelancermap', countries: 'DE', needsKey: null, fetch: fetchFreelancermap },
};
@@ -37,7 +36,6 @@ export function sourceReady(id, secrets) {
const needs = SOURCES[id]?.needsKey;
if (!needs) return true;
if (needs === 'adzuna') return Boolean(secrets.adzunaId && secrets.adzunaKey);
if (needs === 'jooble') return Boolean(secrets.joobleKey);
return false;
}
@@ -291,38 +289,6 @@ async function fetchAdzuna(keyword, { maxAge, secrets }) {
return jobs.filter((job) => job.title && job.url);
}
/* ------------------------------------------------------------------------
* Jooble - kostenloser API-Schluessel (jooble.org/api/about), Sammler fuer DACH.
* Je Land ein eigener Host.
* ---------------------------------------------------------------------- */
async function fetchJooble(keyword, { maxAge, secrets }) {
const since = new Date(Date.now() - maxAge * 24 * 3_600_000).toISOString().slice(0, 10);
const jobs = [];
for (const country of ['de', 'at', 'ch']) {
const data = await request(`https://${country}.jooble.org/api/${encodeURIComponent(secrets.joobleKey)}`, {
json: true,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ keywords: keyword, datecreatedfrom: since, page: '1' }),
});
for (const job of data?.jobs ?? []) {
jobs.push({
source: 'jooble',
externalId: String(job.id ?? job.link),
title: clean(job.title).replace(/<[^>]+>/g, ''),
company: clean(job.company) || null,
location: clean(job.location) || null,
country: country.toUpperCase(),
employmentType: clean(job.type) || null,
url: job.link,
description: clean(job.snippet).replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ') || null,
publishedAt: toDate(job.updated),
});
}
}
return jobs.filter((job) => job.title && job.url);
}
/* ------------------------------------------------------------------------
* freelancermap - Projekte fuer Freiberufler und Interim, neueste zuerst.
* Selektoren wie im Scanner des perinco CRM.