diff --git a/Dockerfile b/Dockerfile index 1167bb7..a2b3fb3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,6 +31,7 @@ COPY server.js ./ COPY lib ./lib COPY views ./views COPY public ./public +COPY extension ./extension # Persisted SQLite data lives here; make it writable for the non-root user RUN mkdir -p /app/data && chown -R node:node /app diff --git a/extension/README.md b/extension/README.md index 9b37c81..f163b88 100644 --- a/extension/README.md +++ b/extension/README.md @@ -1,50 +1,61 @@ -# NextJobs – Indeed Import (Browser-Erweiterung) +# NextJobs – Stellen-Import (Browser-Erweiterung) -Chromium-Erweiterung (Manifest V3), die auf Indeed einen Button **direkt neben der -Stellenbeschreibung** einfügt. Ein Klick sendet alle Stelleninformationen an den -NextJobs. Dort wird die Stelle als **Entwurf** angelegt und die KI -erstellt automatisch zugeschnittene Bewerbungsunterlagen (auf Basis der hinterlegten -Vorlagen). +Chromium-Erweiterung (Manifest V3), die **auf jeder Webseite** Stellen +ausliest und an NextJobs (`https://nextjobs.cc`) sendet. Auf Indeed wird +zusätzlich ein Button *direkt neben der Stellenbeschreibung* eingeblendet. +Die Stelle wird im Tracker als **Entwurf** angelegt; die KI erstellt daraus +zugeschnittene Bewerbungsunterlagen (auf Basis der hinterlegten Vorlagen). + +## Multi-User & Authentifizierung + +Die Tracker-Adresse ist fest `https://nextjobs.cc` (hardcodiert). Jeder +Benutzer authentifiziert sich mit seinem **persönlichen REST-API-Key**: + +- Im Tracker unter **Einstellungen → API-Token (X-API-Key)** einen Token + setzen (falls noch keiner da ist: neu vergeben). +- In der Erweiterung auf das Symbol klicken → **Einstellungen** → Token + eintragen → **Speichern** + **Verbindung testen**. + +Der Key wird im `X-API-Key`-Header gesendet; der Server ordnet den Import dem +Benutzerkonto zu, das diesen Token hinterlegt hat. Ohne (oder mit ungültigem) +Key antwortet der Server mit **401**. Der Key wird pro Browser-Profil in der +`chrome.storage.sync` gespeichert — wer mehrere Konten verwaltet, kann den Key +jederzeit in den Einstellungen wechseln. ## Installation (Entwicklermodus) -1. NextJobs starten (Standard: `http://localhost:3000`). -2. In Chrome/Edge/Brave `chrome://extensions` öffnen. -3. **Entwicklermodus** oben rechts aktivieren. -4. **Entpackte Erweiterung laden** klicken und diesen `extension/`-Ordner auswählen. - -## Konfiguration - -Auf das Erweiterungs-Symbol klicken und die **Tracker-Adresse** setzen -(z. B. `http://localhost:3000` oder die Adresse deiner Deployment-Instanz). -Mit **Verbindung testen** prüfen, ob der Tracker erreichbar ist. +1. In Chrome/Edge/Brave `chrome://extensions` öffnen. +2. **Entwicklermodus** oben rechts aktivieren. +3. **Entpackte Erweiterung laden** klicken und diesen `extension/`-Ordner + auswählen. +4. API-Key eintragen (siehe oben). ## Verwendung -1. Auf `indeed.com` eine Stelle öffnen. -2. Neben der Überschrift **„Vollständige Stellenbeschreibung"** erscheint der grüne - Button **„An NextJobs senden"**. -3. Klicken → die Stelle wird als Entwurf angelegt, die Unterlagen werden im - Hintergrund generiert. Über den Link „Entwurf öffnen" gelangst du direkt zur - Bewerbung im Tracker. +1. Auf einer Stellenanzeige das Erweiterungs-Symbol klicken. Die Felder werden + automatisch ausgelesen (Schema.org/JSON-LD → OpenGraph → Auswahl/Text). + Text auf der Seite markieren → wird als Beschreibung übernommen. +2. Felder prüfen, ggf. korrigieren und **An Tracker senden** klicken. + Auf Indeed alternativ den grünen Button **„An NextJobs senden"** neben + der Stellenbeschreibung. +3. Über den Link **Entwurf öffnen** gelangst du direkt zur Bewerbung im Tracker. ## Voraussetzungen im Tracker -- Unter **Vorlagen** mindestens ein Basis-Dokument (z. B. Anschreiben, Lebenslauf) - hinterlegen — diese dienen der KI als Faktengrundlage. -- `OLLAMA_API_KEY` (Ollama Cloud) gesetzt – z. B. in der `.env`-Datei des Trackers –, - damit die KI-Generierung läuft. +- Unter **Vorlagen** mindestens ein Basis-Dokument (z. B. Anschreiben, + Lebenslauf) hinterlegen — diese dienen der KI als Faktengrundlage. +- `OLLAMA_API_KEY` (Ollama Cloud) gesetzt – z. B. in den Einstellungen des + Benutzers – damit die KI-Generierung läuft. +- Ein **API-Token (X-API-Key)** für den benutzten Account. -## Ausgelesene Felder +## Berechtigungen -| Feld | Quelle (Indeed DOM) | -|------|---------------------| -| Stelle | `[data-testid="jobsearch-JobInfoHeader-title"]` | -| Firma | `[data-testid="inlineHeader-companyName"]` (im `jobsearch-CompanyInfoContainer`) | -| Ort | `[data-testid="jobsearch-JobInfoHeader-companyLocation"]` | -| Gehalt/Art | `#salaryInfoAndJobType` | -| Stellenbeschreibung | `#jobDescriptionText` | -| Quelle-URL | aus `vjk`/`jk`-Parameter bzw. aktueller URL | +| Berechtigung | Zweck | +|---|---| +| `storage` | API-Key speichern (`chrome.storage.sync`) | +| `scripting` + `activeTab` | Stelle auf der aktiven Seite auslesen (nur auf Klick) | +| `https://nextjobs.cc/*` | Import an den Tracker senden | +| `*://*.indeed.com/*` | Inline-Button auf Indeed einblenden | -Indeed ist eine Single-Page-App; die Erweiterung erkennt Wechsel der geöffneten -Stelle per `MutationObserver` und hält den Button aktuell. +Die Erweiterung liest nur die aktive Seite aus, wenn du sie aufrufst, und +sendet die Daten ausschließlich an `https://nextjobs.cc`. \ No newline at end of file diff --git a/extension/background.js b/extension/background.js index 42e7515..94071f8 100644 --- a/extension/background.js +++ b/extension/background.js @@ -1,15 +1,18 @@ -// Service worker: performs the cross-origin POST to the NextJobs. -// Routing the request through the background worker (instead of the content -// script) keeps it independent of the Indeed page's Content-Security-Policy. +// Service worker: performs the cross-origin POST to NextJobs. +// Routing the request through the background worker (instead of a content +// script) keeps it independent of the page's Content-Security-Policy. +// +// Die Tracker-Adresse ist fest (https://nextjobs.cc). Die Authentifizierung +// läuft über den persönlichen REST-API-Key (Header X-API-Key), den jeder +// Benutzer im Popup hinterlegt — der Server ordnet den Import damit dem +// richtigen Benutzerkonto zu. Ohne Key lehnt der Server mit 401 ab. -const DEFAULT_TRACKER_URL = 'http://localhost:3000'; +const TRACKER_URL = 'https://nextjobs.cc'; -function getTrackerUrl() { +function getApiKey() { return new Promise((resolve) => { - chrome.storage.sync.get({ trackerUrl: DEFAULT_TRACKER_URL }, (items) => { - let url = (items.trackerUrl || DEFAULT_TRACKER_URL).trim(); - url = url.replace(/\/+$/, ''); // strip trailing slashes - resolve(url); + chrome.storage.sync.get({ apiKey: '' }, (items) => { + resolve((items.apiKey || '').trim()); }); }); } @@ -18,10 +21,14 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message && message.type === 'IMPORT_JOB') { (async () => { try { - const base = await getTrackerUrl(); - const res = await fetch(base + '/api/indeed-import', { + const apiKey = await getApiKey(); + if (!apiKey) { + sendResponse({ ok: false, needKey: true, error: 'Kein API-Key hinterlegt.' }); + return; + } + const res = await fetch(TRACKER_URL + '/api/indeed-import', { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey }, body: JSON.stringify(message.payload), }); @@ -39,6 +46,10 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { }); return; } + if (res.status === 401) { + sendResponse({ ok: false, needKey: true, error: 'API-Key abgelehnt (401). Bitte in den Einstellungen prüfen.' }); + return; + } sendResponse({ ok: false, error: (data && data.error) || `Server antwortete mit ${res.status}`, @@ -50,17 +61,17 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { ok: true, id: data.id, message: data.message, - openUrl: data.url ? base + data.url : null, + openUrl: data.url ? TRACKER_URL + data.url : null, }); } catch (err) { sendResponse({ ok: false, error: - 'Konnte den NextJobs nicht erreichen. Läuft er, und ist die ' + - 'Adresse in den Erweiterungs-Einstellungen korrekt? (' + String(err.message) + ')', + 'Konnte NextJobs nicht erreichen (' + String(err.message) + '). ' + + 'Prüfe deine Internetverbindung und ob der API-Key in den Einstellungen korrekt ist.', }); } })(); return true; // keep the message channel open for the async response } -}); +}); \ No newline at end of file diff --git a/extension/manifest.json b/extension/manifest.json index f90ec43..3c361d0 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,15 +1,12 @@ { "manifest_version": 3, "name": "NextJobs – Stellen-Import", - "version": "1.1.0", - "description": "Erfasst Stellen von jeder Webseite (nicht nur Indeed) über das Erweiterungs-Popup und sendet sie an den NextJobs, der automatisch zugeschnittene Bewerbungsunterlagen erstellt.", + "version": "2.0.0", + "description": "Erfasst Stellen von jeder Webseite (nicht nur Indeed) über das Erweiterungs-Popup und sendet sie an NextJobs (https://nextjobs.cc), der automatisch zugeschnittene Bewerbungsunterlagen erstellt. Authentifizierung über deinen persönlichen REST-API-Key.", "permissions": ["storage", "scripting", "activeTab"], "host_permissions": [ - "*://*.indeed.com/*", - "http://localhost/*", - "http://127.0.0.1/*", - "http://*/*", - "https://*/*" + "https://nextjobs.cc/*", + "*://*.indeed.com/*" ], "content_scripts": [ { @@ -26,4 +23,4 @@ "default_popup": "popup.html", "default_title": "NextJobs" } -} +} \ No newline at end of file diff --git a/extension/popup.html b/extension/popup.html index 626e68a..7f6c162 100644 --- a/extension/popup.html +++ b/extension/popup.html @@ -94,10 +94,11 @@
-Deinen persönlichen Key findest du im Tracker unter Einstellungen → API-Token (X-API-Key). Pro Benutzer eigener Key.
NextJobs · © nextjobs.cc <%= new Date().getFullYear() %>
+