Carry firma_slug on job offers, supplied by the ingesting client

The blacklist company slug now lives on the offers themselves. POST /joboffers
accepts firma_slug and stores it as supplied (deriving it from firma only when
omitted); it is persisted on jobangebote (schema + ALTER/backfill + index) and
returned in the Joboffer response. Blacklist matching prefers the client-supplied
slug over one derived from firma, so the indexing client controls the identity
used to block an offer. OpenAPI request/response schemas updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 22:42:41 +02:00
co-authored by Claude Opus 4.8
parent f935e00b53
commit 22f967c753
4 changed files with 28 additions and 5 deletions
+10 -4
View File
@@ -633,7 +633,12 @@ function createExternalApi(deps) {
const urlNorm = blacklist.normalizeUrl(b.quelle_url || '') || null;
const anzeigeDatum = sanitizeInput(b.anzeige_datum || '');
const kontaktEmail = sanitizeInput(b.kontakt_email || '');
// Column order matches the UPDATE/INSERT statements below (url_norm last).
// Company slug: stored as supplied by the client; derived from firma only
// when omitted. This is the key the blacklist matches on (see below).
const firmaSlug = b.firma_slug != null && String(b.firma_slug).trim() !== ''
? sanitizeInput(String(b.firma_slug))
: (blacklist.firmaSlug(b.firma) || null);
// Column order matches the UPDATE/INSERT statements below (firma_slug last).
const fields = [
sanitizeInput(b.firma),
sanitizeInput(b.stelle),
@@ -649,6 +654,7 @@ function createExternalApi(deps) {
sanitizeInput(b.status || 'offen'),
serializeLabels(b.labels),
urlNorm,
firmaSlug,
];
// De-dup: prefer a (quelle, external_id) match, else the same normalized
@@ -671,7 +677,7 @@ function createExternalApi(deps) {
await dbRun(
`UPDATE jobangebote SET firma = ?, stelle = ?, ort = ?, adresse = ?, ansprechpartner = ?, gehalt = ?,
beschreibung = ?, quelle_url = ?, art = ?, anzeige_datum = ?, kontakt_email = ?, status = ?,
labels = ?, url_norm = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`,
labels = ?, url_norm = ?, firma_slug = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`,
[...fields, existing.id]
);
const row = await dbGet('SELECT * FROM jobangebote WHERE id = ?', [existing.id]);
@@ -680,8 +686,8 @@ function createExternalApi(deps) {
const result = await dbRun(
`INSERT INTO jobangebote
(external_id, quelle, firma, stelle, ort, adresse, ansprechpartner, gehalt, beschreibung, quelle_url, art, anzeige_datum, kontakt_email, status, labels, url_norm)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
(external_id, quelle, firma, stelle, ort, adresse, ansprechpartner, gehalt, beschreibung, quelle_url, art, anzeige_datum, kontakt_email, status, labels, url_norm, firma_slug)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[externalId, quelle, ...fields]
);
const row = await dbGet('SELECT * FROM jobangebote WHERE id = ?', [result.lastID]);