chore(scanner): alte Scanner-App zurueckgebaut
Die eigenstaendige Vorgaenger-App unter scanner.perinco.de ist auf dem Server entfernt: Unit project-scanner-backend (Port 5005), beide Apache-Vhosts, das Let's-Encrypt-Zertifikat samt Renewal-Config, /var/www/Projekt Scanner (486 MB) und die Datenbank project_scanner (Dump unter /root/backups). Damit entfallen die LEGACY_*-Variablen und das Importskript, dessen Quelle es nicht mehr gibt. /root/.cache/ms-playwright bleibt bewusst stehen: der Cache stammt aus der Installation der alten App, liegt aber ausserhalb beider Verzeichnisse und wird vom CRM-Scanner benutzt - playwright ist auf 1.60.0 gepinnt, damit es genau dieser Build (chromium-1223) bleibt. Der Hinweis steht jetzt in README und docs/06-betrieb.md, damit ihn beim naechsten Aufraeumen niemand mitnimmt. SCANNER_SCHEDULER_OFFSET geht auf 0: der Versatz sollte verhindern, dass beide Instanzen mit denselben Portal-Cookies gleichzeitig losziehen. Ohne zweite Instanz hat er keinen Zweck mehr, die Mechanik bleibt fuer kuenftige Erststarts im Code. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ab6b9f2372
commit
3c9d83161e
@@ -211,22 +211,13 @@ starten absteigend, Text aufsteigend; leere Werte landen immer hinten.
|
||||
|
||||
## Projekt Scanner
|
||||
|
||||
Der eigenständige *Projekt Scanner* ist vollständig hier eingezogen und soll
|
||||
die alte App ersetzen. Die läuft weiter unter `scanner.perinco.de`:
|
||||
Der eigenständige *Projekt Scanner* ist vollständig hier eingezogen und hat die
|
||||
alte App unter `scanner.perinco.de` am **09.09.2026** ersetzt. Seither läuft nur
|
||||
noch diese Instanz; der Rückbau ist in
|
||||
[docs/03-scanner.md](docs/03-scanner.md#rückbau-der-alten-app---09092026)
|
||||
protokolliert.
|
||||
|
||||
```
|
||||
/var/www/Projekt Scanner/backend Express auf 0.0.0.0:5005
|
||||
/var/www/Projekt Scanner/frontend/dist DocumentRoot des Vhosts
|
||||
systemd-Unit: project-scanner-backend (nicht "project-scanner")
|
||||
```
|
||||
|
||||
Bis zur Ablosung laufen **beide parallel und beide voll funktional**: eigene
|
||||
Datenbank, eigener Verschlüsselungsschlüssel, eigenes Upload-Verzeichnis,
|
||||
eigener Scheduler. Die alte App bleibt unverändert - weder ihre Dateien noch
|
||||
ihre Datenbank werden angefasst.
|
||||
|
||||
Menüpunkt **Scanner** links; ein Klick klappt die Reiter der alten App darunter
|
||||
auf:
|
||||
Menüpunkt **Scanner** links; ein Klick klappt die Reiter darunter auf:
|
||||
|
||||
| Reiter | Inhalt |
|
||||
| --- | --- |
|
||||
@@ -248,7 +239,6 @@ beiden Stellen ändern; es ist derselbe Wert in der Datenbank.
|
||||
| Scraper (Playwright), Matching (OpenAI `gpt-4o-mini`), Pipeline, Scheduler, Cleanup, PDF-Parser | `server/src/scanner/services/` |
|
||||
| REST-API | `server/src/routes/scanner.js`, gemountet als `/api/scanner` |
|
||||
| Oberfläche | `src/components/scanner/`, in Tailwind und CI-Farben neu gebaut |
|
||||
| Übernahme aus der alten DB | `server/src/scanner/importLegacy.js` (`npm run scanner:import`) |
|
||||
|
||||
Drei bewusste Unterschiede zum Original:
|
||||
|
||||
@@ -259,19 +249,23 @@ Drei bewusste Unterschiede zum Original:
|
||||
mitschicken, deshalb holt das Frontend die Datei und öffnet sie als Blob.
|
||||
3. **Meldungen** laufen als Toast statt `alert()`, Löschabfragen als Dialog.
|
||||
|
||||
### Parallelbetrieb - alles doppelt
|
||||
### Eigene Ressourcen, eigener Zeitplan
|
||||
|
||||
Beide Scanner laufen **gleichzeitig und funktional**, ohne sich zu berühren.
|
||||
Dafür ist hier jede geteilte Ressource verdoppelt:
|
||||
Der Scanner benutzt durchweg Eigenes und nichts aus der Vorgänger-App - das war
|
||||
die Voraussetzung für den Parallelbetrieb und macht den Rückbau folgenlos:
|
||||
|
||||
| | alte App | diese Instanz |
|
||||
| --- | --- | --- |
|
||||
| Datenbank | `project_scanner` | `perinco_scanner` (eigene Tabellen, gleiche Namen) |
|
||||
| Datenbank | `project_scanner` (gedroppt) | `perinco_scanner` |
|
||||
| Verschlüsselung | `ENCRYPTION_KEY` | `SCANNER_ENCRYPTION_KEY` - eigener Wert |
|
||||
| CV-PDFs | `/var/www/Projekt Scanner/backend/uploads` | `SCANNER_UPLOAD_DIR` = `/var/www/perinco_CRM/server/uploads` |
|
||||
| CV-PDFs | `/var/www/Projekt Scanner/backend/uploads` (gelöscht) | `SCANNER_UPLOAD_DIR` = `/var/www/perinco_CRM/server/uploads` |
|
||||
| Scheduler | eigener Cron im alten Dienst | `SCANNER_SCHEDULER`, Standard `true` |
|
||||
| Aufräumen | 03:00 | 03:30 |
|
||||
|
||||
Geteilt war genau eine Sache, und die liegt ausserhalb beider Verzeichnisse:
|
||||
`/root/.cache/ms-playwright` mit dem Build `chromium-1223`. Der Cache ist beim
|
||||
Rückbau bewusst stehen geblieben, siehe Abschnitt *Playwright*.
|
||||
|
||||
Die Scanner-Datenbank legt das Backend beim Start **selbst an**
|
||||
(`CREATE DATABASE IF NOT EXISTS`), die Tabellen kommen aus `sequelize.sync()`,
|
||||
Portale und Standardeinstellungen werden geseedet. Es genügt also, das Backend
|
||||
@@ -279,28 +273,23 @@ zu starten.
|
||||
|
||||
| Variable | Bedeutung |
|
||||
| --- | --- |
|
||||
| `SCANNER_DB_NAME` | eigene Datenbank, Standard `perinco_scanner` - **nicht** auf `project_scanner` zeigen lassen |
|
||||
| `SCANNER_DB_NAME` | eigene Datenbank, Standard `perinco_scanner` |
|
||||
| `SCANNER_DB_HOST/_PORT/_USER/_PASSWORD` | leer = CRM-Zugangsdaten |
|
||||
| `SCANNER_ENCRYPTION_KEY` | eigener Schlüssel für die Portal-Passwörter; ohne ihn wird ein bekannter Ersatzschlüssel benutzt und beim Start gewarnt |
|
||||
| `SCANNER_UPLOAD_DIR` | eigenes CV-Verzeichnis; leer = `<server>/uploads` |
|
||||
| `SCANNER_ENCRYPTION_KEY` | Schlüssel für die Portal-Passwörter; ohne ihn wird ein bekannter Ersatzschlüssel benutzt und beim Start gewarnt |
|
||||
| `SCANNER_UPLOAD_DIR` | CV-Verzeichnis; leer = `<server>/uploads` |
|
||||
| `SCANNER_SCHEDULER` | Hintergrund-Scraper; Standard an, nur `false` schaltet ab |
|
||||
| `SCANNER_SCHEDULER_OFFSET` | Minuten Versatz für den **allerersten** Lauf, Standard `30`. Danach trägt `settings.last_scan_at` den Zeitplan über Neustarts hinweg |
|
||||
| `SCANNER_SCHEDULER_OFFSET` | Minuten Versatz für den **allerersten** Lauf, seit dem Rückbau `0`. Danach trägt `settings.last_scan_at` den Zeitplan über Neustarts hinweg |
|
||||
|
||||
> **Kosten:** Beide Instanzen bewerten dieselben Projekte unabhängig
|
||||
> voneinander - die OpenAI-Kosten fallen also doppelt an, solange die alte App
|
||||
> läuft. Das ist der Preis des Parallelbetriebs, kein Fehler. Wer nur zuschauen
|
||||
> will, setzt `SCANNER_SCHEDULER=false`; der Knopf *Jetzt scannen* funktioniert
|
||||
> dann weiter.
|
||||
Wer nur zuschauen will, setzt `SCANNER_SCHEDULER=false`; der Knopf *Jetzt
|
||||
scannen* funktioniert dann weiter.
|
||||
|
||||
Der **Zeitversatz** ist nicht Kosmetik: beide Instanzen benutzen dieselben
|
||||
Portal-Sessions (aus derselben Cookie-Quelle) und würden beim ersten Start
|
||||
gleichzeitig losziehen - das provoziert Rate-Limits und CAPTCHAs bei
|
||||
Freelancermap und Freelance.de. `SCANNER_SCHEDULER_OFFSET=30` verschiebt den
|
||||
ersten Lauf um 30 Minuten, danach laufen beide im eigenen Intervall verschränkt.
|
||||
|
||||
Ab dem zweiten Start trägt `settings.last_scan_at` den Zeitplan - die Frist
|
||||
greift dann nicht mehr. Sonst begann sie bei jedem Deployment von vorn und ein
|
||||
fälliger Lauf konnte beliebig lange ausfallen (siehe
|
||||
Der **Zeitversatz** war nicht Kosmetik: beide Instanzen benutzten dieselben
|
||||
Portal-Sessions und wären beim ersten Start gleichzeitig losgezogen - das
|
||||
provoziert Rate-Limits und CAPTCHAs bei Freelancermap und Freelance.de. Mit dem
|
||||
Rückbau steht der Wert auf 0; die Mechanik bleibt im Code, weil sie bei jedem
|
||||
Erststart einer weiteren Instanz wieder greift. Ab dem zweiten Start trägt
|
||||
`settings.last_scan_at` den Zeitplan - sonst begann die Frist bei jedem
|
||||
Deployment von vorn und ein fälliger Lauf konnte beliebig lange ausfallen (siehe
|
||||
[docs/03-scanner.md](docs/03-scanner.md#wann-laeuft-er)).
|
||||
|
||||
Innerhalb dieser Instanz kann **nur ein Lauf** gleichzeitig arbeiten: Scheduler
|
||||
@@ -309,57 +298,41 @@ Aufruf bekommt 409 und den Hinweis *„Ein Suchlauf läuft bereits"*. Das Dashbo
|
||||
zeigt oben links den Zustand - automatische Suche aktiv, nur manuelle Suche,
|
||||
pausiert, Suchlauf läuft - samt Namen der Datenbank.
|
||||
|
||||
### Daten aus der alten App übernehmen
|
||||
### Was aus der alten App wurde
|
||||
|
||||
Damit die neue Instanz nicht bei Null anfängt, holt ein Skript Agenten, Portale
|
||||
und Einstellungen herüber. Es liest die alte Datenbank nur, geschrieben wird
|
||||
ausschließlich in `perinco_scanner`:
|
||||
Beim Rollout am 06.08.2026 hat ein Skript (`importLegacy.js`,
|
||||
`npm run scanner:import`) Agenten, Portale, Einstellungen und Session-Cookies aus
|
||||
`project_scanner` herübergeholt; die CV-PDFs wurden in das eigene
|
||||
Upload-Verzeichnis kopiert. Mit dem Rückbau am 09.09.2026 sind Quelle und Skript
|
||||
weg.
|
||||
|
||||
```bash
|
||||
cd /var/www/perinco_CRM/server
|
||||
npm run scanner:import -- --dry-run # nur anzeigen
|
||||
npm run scanner:import # Einstellungen, Portale, Agenten + CVs
|
||||
npm run scanner:import -- --with-matches # zusätzlich Projekte und Treffer
|
||||
npm run scanner:import -- --force # vorhandene Zieldaten vorher löschen
|
||||
npm run scanner:import -- --agents-only --skip=Bilal # später: nur fehlende Agenten
|
||||
```
|
||||
> **Damals festgestellt:** Die in der alten Datenbank gespeicherten
|
||||
> Portal-Passwörter liessen sich mit dem dortigen `ENCRYPTION_KEY` **nicht** mehr
|
||||
> entschlüsseln - der Schlüssel war nach dem Speichern getauscht worden. Die alte
|
||||
> App konnte sie damit ebenso wenig lesen, es ging also nichts verloren. Die
|
||||
> Sessions (Cookies) lagen im Klartext und wurden übernommen.
|
||||
|
||||
`--agents-only` ist der nachträgliche Lauf: nur Suchagenten, additiv, vorhandene
|
||||
werden über den Namen erkannt und übersprungen. `--skip=` lässt Agenten nach
|
||||
Namen aus. Details: [03 Projekt Scanner](docs/03-scanner.md#agenten-nachträglich-holen).
|
||||
|
||||
Quelle kommt aus `LEGACY_*` in `server/.env`:
|
||||
|
||||
| Variable | Wofür |
|
||||
| --- | --- |
|
||||
| `LEGACY_DB_NAME` | Standard `project_scanner` |
|
||||
| `LEGACY_DB_HOST/_PORT/_USER/_PASSWORD` | leer = CRM-Zugangsdaten |
|
||||
| `LEGACY_UPLOAD_DIR` | Verzeichnis der alten CV-PDFs (`/var/www/Projekt Scanner/backend/uploads`); die Dateien werden **kopiert**. Wegen des Leerzeichens im Pfad in der `.env` in Anführungszeichen setzen - systemd liest die Datei als `EnvironmentFile` und trennt sonst am Leerzeichen |
|
||||
| `LEGACY_ENCRYPTION_KEY` | nur damit lassen sich die Portal-Passwörter übernehmen: entschlüsseln mit dem alten, neu verschlüsseln mit dem eigenen Schlüssel |
|
||||
|
||||
Übernommen werden auch die Session-Cookies der Portale (Klartext-JSON in der
|
||||
DB) - der neue Scanner ist damit sofort scharf.
|
||||
|
||||
Agenten, Projekte und Treffer behalten ihre Ids, damit übernommene Treffer
|
||||
weiter auf die richtigen Agenten und Projekte zeigen. Deshalb bricht das Skript
|
||||
ab, wenn im Ziel schon Agenten stehen - `--force` räumt vorher auf.
|
||||
|
||||
> **Beim Rollout am 06.08.2026 festgestellt:** Die in der alten Datenbank
|
||||
> gespeicherten Portal-Passwörter lassen sich mit dem dortigen
|
||||
> `ENCRYPTION_KEY` **nicht** mehr entschlüsseln - der Schlüssel wurde nach dem
|
||||
> Speichern getauscht. Die alte App kann sie damit ebenso wenig lesen; es ging
|
||||
> also nichts verloren. Wer den Notfall-Login des Scrapers will, gibt die
|
||||
> Passwörter unter *Börsen-Konten* neu ein. Die Sessions (Cookies) sind davon
|
||||
> unberührt und wurden übernommen.
|
||||
Nicht mitgekommen sind 13380 Treffer, die es nur in der alten Datenbank gab,
|
||||
darunter 92 als *reviewed* und 5 als *applied* markierte. Ein Nachimport hätte
|
||||
mit den vorhandenen Ids in `perinco_scanner` kollidiert, und der Bestand war
|
||||
ohnehin flüchtig - der Aufräumjob hält nur die jüngsten Tage. Ein Dump liegt
|
||||
unter `/root/backups/project_scanner_20260909-173447.sql.gz`.
|
||||
|
||||
### Playwright
|
||||
|
||||
`playwright` ist auf **exakt 1.60.0** gepinnt - dieselbe Version wie die laufende
|
||||
Scanner-App. Deren Chromium-Build (`chromium-1223`) liegt auf dem Server bereits
|
||||
in `/root/.cache/ms-playwright`, also fällt kein zweiter Browser-Download an und
|
||||
gescrapt wird mit der Version, die dort produktiv funktioniert. Ein `^` in der
|
||||
Version würde beides aufgeben, deshalb steht in `server/package.json` ein
|
||||
`comments`-Feld mit dieser Begründung.
|
||||
`playwright` ist auf **exakt 1.60.0** gepinnt. Der passende Chromium-Build
|
||||
(`chromium-1223`) liegt auf dem Server in `/root/.cache/ms-playwright`, also
|
||||
fällt kein zweiter Browser-Download an und gescrapt wird mit der Version, die
|
||||
dort produktiv funktioniert. Ein `^` in der Version würde beides aufgeben,
|
||||
deshalb steht in `server/package.json` ein `comments`-Feld mit dieser
|
||||
Begründung.
|
||||
|
||||
> **Nicht mit aufräumen:** Dieser Cache (641 MB) stammt aus der Installation der
|
||||
> alten Scanner-App, liegt aber ausserhalb beider Anwendungsverzeichnisse und
|
||||
> wurde von beiden benutzt - `PLAYWRIGHT_BROWSERS_PATH` ist nirgends gesetzt.
|
||||
> Beim Rückbau am 09.09.2026 ist er deshalb bewusst stehen geblieben. Wer ihn
|
||||
> löscht, nimmt dem Scanner den Browser; Reparatur:
|
||||
> `cd /var/www/perinco_CRM/server && npx playwright install chromium`.
|
||||
|
||||
## Diktat (Whisper im Browser)
|
||||
|
||||
@@ -601,16 +574,10 @@ Ein Rollout mit neuen Backend-Paketen braucht `-Deps`.
|
||||
### Einmalige Scanner-Einrichtung (am 06.08.2026 erledigt)
|
||||
|
||||
```bash
|
||||
# 1. .env ergänzen: erzeugt SCANNER_ENCRYPTION_KEY, holt die LEGACY_*-Werte
|
||||
# aus "/var/www/Projekt Scanner/backend/.env", legt vorher ein .env.bak an
|
||||
# 1. .env ergänzen: erzeugt SCANNER_ENCRYPTION_KEY, legt vorher ein .env.bak an
|
||||
bash /tmp/scanner-setup.sh # aus deploy/scanner-setup.sh
|
||||
|
||||
# 2. Bestand übernehmen
|
||||
cd /var/www/perinco_CRM/server
|
||||
npm run scanner:import -- --dry-run
|
||||
npm run scanner:import -- --with-matches
|
||||
|
||||
# 3. MemoryMax anheben und neu starten
|
||||
# 2. MemoryMax anheben und neu starten
|
||||
cp /var/www/perinco_CRM/deploy/perinco-crm-backend.service /etc/systemd/system/
|
||||
systemctl daemon-reload && systemctl restart perinco-crm-backend
|
||||
journalctl -u perinco-crm-backend -n 40
|
||||
@@ -618,26 +585,14 @@ journalctl -u perinco-crm-backend -n 40
|
||||
|
||||
Die Datenbank `perinco_scanner` muss **nicht** von Hand angelegt werden, das
|
||||
erledigt das Backend beim Start. Playwright-Browser ebenfalls nicht, siehe
|
||||
Abschnitt *Playwright* oben.
|
||||
|
||||
> **Reihenfolge beachten:** Der Import schreibt `search_interval` aus der alten
|
||||
> App (15 Minuten) in die Einstellungen. Der laufende Scheduler bemerkt die
|
||||
> Änderung binnen einer Minute und plant sich neu - beim Rollout hat er deshalb
|
||||
> direkt nach dem Import einen Scan gestartet, den der folgende Restart mitten im
|
||||
> Scrapen abgeschossen hat (`Target page ... has been closed` im Log, harmlos).
|
||||
> Wer das vermeiden will, macht Schritt 3 vor Schritt 2.
|
||||
Abschnitt *Playwright* oben. Der damalige Schritt *Bestand übernehmen*
|
||||
(`npm run scanner:import`) ist mit dem Rückbau der alten App entfallen.
|
||||
|
||||
Das höhere `MemoryMax` (1536M statt 512M) ist kein Luxus: ein Scan-Lauf startet
|
||||
Chromium als Kindprozess in derselben cgroup, mit 512M würde der OOM-Killer das
|
||||
Backend mitten im Scrapen beenden. `deploy.ps1` lädt die Unit nicht mit hoch -
|
||||
daher der `cp` oben.
|
||||
|
||||
> **RAM im Parallelbetrieb:** Im schlechtesten Fall scrapen beide Scanner
|
||||
> gleichzeitig, also laufen zwei Chromium-Instanzen. `SCANNER_SCHEDULER_OFFSET`
|
||||
> verschiebt die Läufe gegeneinander, garantiert aber nichts - der alte Dienst
|
||||
> hat seinen eigenen Zeitplan. Auf einem Server mit knappem RAM und vollem Swap
|
||||
> ist das der Punkt, den man nach dem ersten Tag im Journal nachsieht.
|
||||
|
||||
Manuell entspricht das:
|
||||
|
||||
```powershell
|
||||
|
||||
Reference in New Issue
Block a user