Add AI application assistant: Indeed import + Ollama document generation
- Browser extension (Chromium MV3) injecting a "send to tracker" button next to the Indeed job description; scrapes job info and posts it to a new /api/indeed-import endpoint (CORS-enabled), configurable tracker URL via popup. - New "Entwurf" status. Imports create a draft and trigger background AI generation of tailored Anschreiben + Lebenslauf (PDF attachments) via the Ollama Cloud API, grounded strictly in user-provided base documents. - Vorlagen page to manage base documents; attachments UI, generation status polling, regenerate and download routes on the application page. - Schema: ort/stellenbeschreibung/quelle_url/generierung_* columns, plus basis_dokumente and anhaenge tables (with migrations). - Config via .env (OLLAMA_API_KEY/OLLAMA_MODEL/OLLAMA_HOST); dependency-free .env loader. Dockerfile copies lib/, .dockerignore added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
// Service worker: performs the cross-origin POST to the Bewerbungs-Tracker.
|
||||
// Routing the request through the background worker (instead of the content
|
||||
// script) keeps it independent of the Indeed page's Content-Security-Policy.
|
||||
|
||||
const DEFAULT_TRACKER_URL = 'http://localhost:3000';
|
||||
|
||||
function getTrackerUrl() {
|
||||
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.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', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(message.payload),
|
||||
});
|
||||
|
||||
let data = {};
|
||||
try { data = await res.json(); } catch (_) { /* non-JSON response */ }
|
||||
|
||||
if (!res.ok) {
|
||||
sendResponse({
|
||||
ok: false,
|
||||
error: (data && data.error) || `Server antwortete mit ${res.status}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse({
|
||||
ok: true,
|
||||
id: data.id,
|
||||
message: data.message,
|
||||
openUrl: data.url ? base + data.url : null,
|
||||
});
|
||||
} catch (err) {
|
||||
sendResponse({
|
||||
ok: false,
|
||||
error:
|
||||
'Konnte den Bewerbungs-Tracker nicht erreichen. Läuft er, und ist die ' +
|
||||
'Adresse in den Erweiterungs-Einstellungen korrekt? (' + String(err.message) + ')',
|
||||
});
|
||||
}
|
||||
})();
|
||||
return true; // keep the message channel open for the async response
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user