6 Commits
Author SHA1 Message Date
thomasandClaude ce844412e5 Jobsuche: Agent pro Benutzer in isoliertem Docker-Container
Jeder Suchlauf läuft nun in einem frischen Container pro Benutzer, dessen
pro-Benutzer-Home als ~/.claude gemountet ist — Memories und Session-Contexte
liegen damit strikt getrennt pro Benutzer. Die Such-Skills sind Shared-Code
aus dem Image und werden im Container nur nach ~/.claude/skills verlinkt.

Der Host-Runner startet pro Lauf `docker run --rm` und führt bis zu
JOBSUCHE_MAX_PARALLEL (Default 4) Läufe parallel über verschiedene Benutzer
aus (jeder hat eigenen Ollama-Key = getrennte Rate-Limits). Der alte gemeinsame
~/.claude-Pfad wird vom Runner nicht mehr beschrieben.

- source/agent/: neues Agent-Image (Dockerfile + entrypoint + drei Skills)
- scripts/jobsuche-runner.js: agentStarten als docker run, ensureAgentDir, runPool
- scripts/jobsuche-runner.sh + bin/-Kopie: Image-Guard
- package.json: docker:build-agent (lokal, ohne Registry-Push)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 15:41:22 +02:00
thomasandClaude bf789af62a Rebranding zu NextJobs (nextjobs.cc)
- Markenname 'Bewerbungs-Tracker' -> 'NextJobs' im gesamten UI,
  Footer (incl. nextjobs.cc Copyright), Login, Wortmarke in der
  Top-Navi (Next + Jobs-Gradient), Favicon, Swagger/OpenAPI-Titel,
  CalDAV-PRODID, Browser-Erweiterung (manifest, content, background),
  Runner-Log und Doku.
- Funktionale Begriffe (Bewerbungsunterlagen, -datum etc.) bleiben.
- Docker-Registry (git.hackner.dev) als Infrastruktur unberuehrt.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 01:40:26 +02:00
thomasandClaude 17d438d102 Jobsuche-Runner: nvm laden, damit Cron node findet
Node laeuft unter nvm, dessen bin-Verzeichnis nicht im Cron-PATH
lag. Jeder Cron-Tick schlug mit `node: command not found` fehl,
weshalb manuell angeforderte Suchlaeufe ewig im Status 'angefordert'
(wartet) haengen blieben. nvm.sh wird jetzt vor dem Aufruf gesourced.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 01:04:59 +02:00
thomasandClaude Opus 4.8 1aa902bdce Top-Navigation: neu strukturiert, mit Aktiv-Zustand und Dropdowns
Die Leiste war eine flache Reihe aus sieben gleichrangigen Pillen. Zwei
Probleme: sie zeigte nie an, wo man gerade ist (es gab schlicht keinen
Aktiv-Zustand), und auf dem Handy fielen nur die Beschriftungen weg - uebrig
blieben sieben unbeschriftete Icons.

Struktur folgt jetzt dem tatsaechlichen Ablauf (finden -> bewerben ->
antworten) statt der Reihenfolge, in der die Seiten entstanden sind:

  Bewerbungen | Jobangebote | Stellensuche v | Postfach | Vorlagen

"Stellensuche" buendelt als Dropdown, was Angebote erzeugt und filtert:
Suchprofil & Laeufe, Uebernommene Angebote, Blacklist - jeweils mit einer Zeile,
die erklaert, wozu der Punkt gut ist. Einstellungen, Benutzerverwaltung und
Abmelden wandern ins Konto-Menue rechts, wo Systemkram hingehoert. Die
taeglichen Wege (Bewerbungen, Jobangebote, Postfach) bleiben ein Klick weit weg.

Gestaltung: Die Leiste behaelt in beiden Themes ihr dunkles Navy - sie ist der
Rahmen des Werkzeugs und die einzige Flaeche, die sich beim Navigieren nie
aendert. Alles darin ist bewusst still (Slate auf Navy); der einzige Akzent ist
der aktive Punkt, markiert durch eine schmale leuchtende Schiene an der
Unterkante. Beim Hover deutet sich dieselbe Schiene gedaempft an.

Zaehler (ungelesene Post, offene Angebote) haengen jetzt an Klassen statt IDs,
weil sie in Leiste UND Mobilmenue erscheinen. Ein Controller bedient alle
Dropdowns: Klick oeffnet, andere schliessen, Escape und Klick daneben schliessen,
aria-expanded bleibt ehrlich. Fokus ist sichtbar, prefers-reduced-motion wird
respektiert, das Mobilmenue zeigt alle Punkte ausgeschrieben und gruppiert.

Der aktive Pfad kommt aus res.locals.pfad (Auth-Middleware). Die Navigation liegt
weiterhin in genau einem Partial - sie ist damit auf allen Seiten identisch;
nur die Login-Seite hat bewusst keine.

Geprueft mit echtem Browser (Screenshots): hell/dunkel, Dropdowns, Mobilmenue,
und der Aktiv-Zustand je Seite - Unterseiten wie /blacklist oder /jobsuche heben
korrekt "Stellensuche" hervor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 23:55:58 +02:00
thomasandClaude Opus 4.8 9418750061 Jobsuche als Feature der App: Suchprofil pro Benutzer statt Cron-Prompt
Die Jobsuche lag in zwei Host-Skripten (jobsuche-cron.sh, jobsuche-remote-cron.sh)
aus der Ein-Benutzer-Zeit: Die Suchkriterien (sieben Staedte, Rollen, Buzzwords)
standen fest im Prompt, und importiert wurde mit EINEM globalen API-Token aus der
.env - der zufaellig dem Admin gehoerte. Auf der Multi-User-Plattform ist beides
hinfaellig.

Jetzt legt jeder Benutzer sein Suchprofil selbst fest (/jobsuche):
- Modus: regional / 100 % Remote / beides
- Staedte (die erste gilt als Wohnort und wird hoechstpriorisiert, max. 12)
- Feinschliff: zusaetzliche Begriffe, Ausschluesse
- Zeitplan: Wochentage + Uhrzeit, plus Button "Jetzt suchen"
Rollen und Technologien bleiben abgeleitet - aus dem Lebenslauf des Benutzers
(basis_dokumente), nicht aus einer gepflegten Liste. Admins koennen Profil und
Zeitplan eines Benutzers ueber /jobsuche?user=<id> mitpflegen (Link im Admin-Panel).

Aufteilung App/Host: Der Container hat weder claude noch ollama. Die App reiht
Laeufe daher nur in die Warteschlange ein (Tabelle suchlaeufe); der neue Runner
auf dem Host (scripts/jobsuche-runner.js, Cron alle 5 Min) arbeitet sie ab, baut
den Prompt je Benutzer aus dessen Profil + Lebenslauf und laeuft mit DESSEN
Zugangsdaten:
- BEWERBUNG_API_KEY = eigener API-Token des Benutzers (wird beim ersten Lauf
  automatisch erzeugt), damit Treffer im richtigen Konto landen und durch dieselbe
  Dedup-/Blacklist-Logik gehen,
- ANTHROPIC_BASE_URL/AUTH_TOKEN = eigener Ollama-Key des Benutzers ueber die
  Anthropic-kompatible Schnittstelle von Ollama Cloud (https://ollama.com/v1/messages,
  verifiziert: gueltiger Key -> 200, ungueltiger -> 401). Damit zahlt jeder seine
  eigene Suche, statt alles ueber die Host-Subscription zu buchen
  (JOBSUCHE_KI_AUTH=host stellt das alte Verhalten wieder her).

Ohne eigenen Ollama-Key oder ohne Lebenslauf bricht der Lauf mit klarer Meldung ab
statt still nichts zu tun; die Oberflaeche warnt vorab. Kein Stapeln: solange ein
Lauf offen ist, erzeugt ein weiterer Klick keinen zweiten. Verwaiste Laeufe
(Prozess weg) werden nach Zeitlimit als Fehler freigegeben.

Verifiziert mit zwei Benutzern: Zugriffsschutz (fremdes Profil -> 403), Speichern,
Warteschlange, automatische Key-Erzeugung, Prompt-Aufbau aus Profil + CV,
Ergebnis-Ruecklauf in die Oberflaeche, Fehlerpfade und die Faelligkeitslogik des
Zeitplans (Tag/Uhrzeit/bereits gelaufen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 23:28:56 +02:00
thomasandClaude 0371aa85a5 Multi-User-Plattform: jeder Benutzer hat eigene, isolierte Daten
- Auth via Session-Cookie + Login-Seite (scrypt, lib/password.js, sessions-Tabelle)
- AsyncLocalStorage (lib/context.js) propagiert aktuellen Benutzer durch alle Libs
- user_id auf allen Datentabellen (FK->users ON DELETE CASCADE), per-user PK/UNIQUE
  (app_state, settings, prompts, design, jobangebote) und per-user Dateispeicher
  (data/<dir>/<userId>/)
- Alle Queries in server.js + lib/api.js nach user_id scope-iert
- Pro-Benutzer-Konfiguration (Ollama/Mail/CalDAV/API-Token) in app_state,
  Live gelesen via config.get(); Hintergrund-Loops (IMAP/CalDAV) iterieren alle Benutzer
- REST-API /api/v1: X-API-Key loest den Token zu einem Benutzer auf, Anfragen
  operieren nur auf dessen Daten
- Admin-Panel /admin: Benutzer anlegen, Passwort zuruecksetzen, loeschen (mit Daten)
- Idempotente Migration (lib/migrate-multiuser.js + scripts/migrate-to-multiuser.js):
  bestehende Daten werden dem Benutzer admin:admin zugeordnet

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-13 21:57:50 +02:00