docs: echte Umlaute in der Dokumentation
893 Stellen in Fliesstext, Tabellen und Mermaid-Beschriftungen. Dazu die sichtbaren ss-Fälle (ausschließlich, groß, außen, schließt, heißt, draußen, weiße, anstoßen, Straße). Nicht angetastet, damit die Doku weiter beschreibt, was wirklich in der Datenbank steht: - Codeblöcke und Code-Spans - dort stehen Bezeichner, SQL, Enum-Werte und Dateinamen: `mail_pool`, `'uebernommen'`, `extract.js` - in Mermaid nur der Text in Anführungszeichen; Knotennamen, Pfeile und Werte wie source = 'Mail' bleiben, wie sie sind - `empfaenger` im ER-Diagramm von crm_mail_contact_candidates: die Aufzählung dort nennt die Datenbankwerte, nicht die Anzeige Die Menü-Skizze in der Benutzeranleitung zeigt jetzt die aufgeklappten Kontakte mit "Aus Mails" samt Zähler. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
8b7ce448a6
commit
7973dc07b1
+39
-39
@@ -1,8 +1,8 @@
|
||||
# 03 Projekt Scanner
|
||||
|
||||
Der Scanner durchsucht Projektboersen nach Auftraegen und bewertet jeden Fund mit
|
||||
OpenAI gegen die Lebenslaeufe der Berater. Er ist eine vollstaendige Portierung
|
||||
der eigenstaendigen App und laeuft parallel zu ihr.
|
||||
Der Scanner durchsucht Projektbörsen nach Aufträgen und bewertet jeden Fund mit
|
||||
OpenAI gegen die Lebensläufe der Berater. Er ist eine vollständige Portierung
|
||||
der eigenständigen App und läuft parallel zu ihr.
|
||||
|
||||
## Ein Suchlauf
|
||||
|
||||
@@ -43,15 +43,15 @@ sequenceDiagram
|
||||
|
||||
Zwei Sparmassnahmen stecken bewusst **vor** dem OpenAI-Aufruf: der vorhandene
|
||||
Treffer (Unique-Key `agent_id, project_id`) und die Ausschlussbegriffe. Ein
|
||||
Projekt, das „Arbeitnehmerueberlassung" enthaelt, kostet damit keinen Cent.
|
||||
Projekt, das „Arbeitnehmerüberlassung" enthält, kostet damit keinen Cent.
|
||||
|
||||
## Wann laeuft er
|
||||
## Wann läuft er
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
T["Cron-Tick, jede Minute"] --> A{"scanning_active?"}
|
||||
A -->|nein| STOP1["nichts tun"]
|
||||
A -->|ja| B{"Intervall geaendert?"}
|
||||
A -->|ja| B{"Intervall geändert?"}
|
||||
B -->|ja| RE["neu planen"]
|
||||
B -->|nein| C{"Sperrfrist<br/>nur beim allerersten Start"}
|
||||
C -->|noch aktiv| STOP2["nichts tun"]
|
||||
@@ -59,7 +59,7 @@ flowchart TD
|
||||
D -->|nein| STOP3["nichts tun"]
|
||||
D -->|ja| E{"07:00-21:59?"}
|
||||
E -->|nein| STOP4["nichts tun"]
|
||||
E -->|ja| S["last_scan_at aus der DB lesen<br/>(faengt manuelle Laeufe mit ein)"]
|
||||
E -->|ja| S["last_scan_at aus der DB lesen<br/>(fängt manuelle Läufe mit ein)"]
|
||||
S --> F{"Intervall seit<br/>letztem Lauf um?"}
|
||||
F -->|nein| STOP5["nichts tun"]
|
||||
F -->|ja| G["Pipeline starten"]
|
||||
@@ -72,23 +72,23 @@ gleichzeitig los, was Rate-Limits und CAPTCHAs provoziert - und zwei Chromium
|
||||
gleichzeitig belasten den Speicher doppelt.
|
||||
|
||||
Der Zeitpunkt des letzten Laufs steht in `settings.last_scan_at`. Das ist kein
|
||||
Luxus, sondern nach zwei beobachteten Fehlern noetig:
|
||||
Luxus, sondern nach zwei beobachteten Fehlern nötig:
|
||||
|
||||
> **Erster Fehler:** Der Versatz war ueber einen vorgealterten `lastRunTime`
|
||||
> gebaut. Ein Zeitstempel kann einen Lauf aber nur **vorziehen**, nie verzoegern -
|
||||
> **Erster Fehler:** Der Versatz war über einen vorgealterten `lastRunTime`
|
||||
> gebaut. Ein Zeitstempel kann einen Lauf aber nur **vorziehen**, nie verzögern -
|
||||
> bei Intervall 15 und Versatz 30 lief er sofort. Seither eine eigene Frist
|
||||
> (`notBefore`).
|
||||
>
|
||||
> **Zweiter Fehler:** Diese Frist begann bei **jedem** Neustart von vorn. Zwei
|
||||
> Deployments im Abstand von zehn Minuten haben den faelligen Lauf zweimal um eine
|
||||
> halbe Stunde verschoben - er fand nie statt. Wer oefter als alle 30 Minuten
|
||||
> deployt, haette einen Scanner, der nie automatisch sucht. Deshalb liegt der
|
||||
> Deployments im Abstand von zehn Minuten haben den fälligen Lauf zweimal um eine
|
||||
> halbe Stunde verschoben - er fand nie statt. Wer öfter als alle 30 Minuten
|
||||
> deployt, hätte einen Scanner, der nie automatisch sucht. Deshalb liegt der
|
||||
> Zeitpunkt jetzt in der Datenbank, und der Versatz greift nur, solange dort noch
|
||||
> nichts steht.
|
||||
|
||||
Der gespeicherte Zeitpunkt wird auch **im Tick** gelesen: ein manueller Scan aus
|
||||
der Oberflaeche schreibt ihn, weiss aber nichts von der Variablen im Scheduler.
|
||||
Ohne den Abgleich koennte der Scheduler direkt hinter einem manuellen Lauf noch
|
||||
der Oberfläche schreibt ihn, weiß aber nichts von der Variablen im Scheduler.
|
||||
Ohne den Abgleich könnte der Scheduler direkt hinter einem manuellen Lauf noch
|
||||
einmal losziehen.
|
||||
|
||||
## Parallelbetrieb - alles verdoppelt
|
||||
@@ -107,7 +107,7 @@ flowchart LR
|
||||
N4["SCANNER_ENCRYPTION_KEY"] --- N1
|
||||
end
|
||||
|
||||
B["Projektboersen"]
|
||||
B["Projektbörsen"]
|
||||
O["OpenAI"]
|
||||
A1 --> B
|
||||
N1 --> B
|
||||
@@ -122,16 +122,16 @@ flowchart LR
|
||||
| | alte App | CRM |
|
||||
| --- | --- | --- |
|
||||
| Datenbank | `project_scanner` | `perinco_scanner` |
|
||||
| Schluessel | `ENCRYPTION_KEY` | `SCANNER_ENCRYPTION_KEY` |
|
||||
| Schlüssel | `ENCRYPTION_KEY` | `SCANNER_ENCRYPTION_KEY` |
|
||||
| CV-Dateien | eigener Ordner | eigener Ordner |
|
||||
| Aufraeumen | 03:00 | 03:30 |
|
||||
| Aufräumen | 03:00 | 03:30 |
|
||||
| Anmeldung | HTTP Basic | CRM-Sitzung (JWT) |
|
||||
|
||||
**Der Preis:** Beide bewerten dieselben Projekte unabhaengig voneinander, die
|
||||
**Der Preis:** Beide bewerten dieselben Projekte unabhängig voneinander, die
|
||||
OpenAI-Kosten fallen also doppelt an. Wer das senken will, stellt im CRM ein
|
||||
groesseres Suchintervall ein - die Einstellung gilt nur fuer diese Instanz.
|
||||
größeres Suchintervall ein - die Einstellung gilt nur für diese Instanz.
|
||||
|
||||
## Uebernahme aus der alten Datenbank
|
||||
## Übernahme aus der alten Datenbank
|
||||
|
||||
`server/src/scanner/importLegacy.js`, aufrufbar als `npm run scanner:import`:
|
||||
|
||||
@@ -140,9 +140,9 @@ flowchart TD
|
||||
S["Start"] --> C{"Zieltabelle agents leer?"}
|
||||
C -->|nein, ohne --force| AB["Abbruch"]
|
||||
C -->|ja oder --force| E["Einstellungen kopieren<br/>inkl. OpenAI-Key"]
|
||||
E --> P["Portale ueber den Namen zuordnen:<br/>Benutzername, Session-Cookies, Status"]
|
||||
E --> P["Portale über den Namen zuordnen:<br/>Benutzername, Session-Cookies, Status"]
|
||||
P --> PW{"LEGACY_ENCRYPTION_KEY gesetzt?"}
|
||||
PW -->|ja| PW2["Passwort entschluesseln<br/>und neu verschluesseln"]
|
||||
PW -->|ja| PW2["Passwort entschlüsseln<br/>und neu verschlüsseln"]
|
||||
PW -->|nein| PW3["Passwort auslassen"]
|
||||
PW2 --> A["Agenten anlegen, Ids behalten<br/>CV-PDF kopieren"]
|
||||
PW3 --> A
|
||||
@@ -152,33 +152,33 @@ flowchart TD
|
||||
M2 --> F
|
||||
```
|
||||
|
||||
Die Ids bleiben erhalten, damit uebernommene Treffer weiter auf die richtigen
|
||||
Die Ids bleiben erhalten, damit übernommene Treffer weiter auf die richtigen
|
||||
Agenten und Projekte zeigen. Deshalb bricht das Skript bei bereits vorhandenen
|
||||
Agenten ab.
|
||||
|
||||
> **Beim Rollout am 06.08.2026 festgestellt:** Die Portal-Passwoerter der alten
|
||||
> **Beim Rollout am 06.08.2026 festgestellt:** Die Portal-Passwörter der alten
|
||||
> Datenbank lassen sich mit dem dortigen `ENCRYPTION_KEY` nicht mehr
|
||||
> entschluesseln - der Schluessel wurde nach dem Speichern getauscht. Die alte App
|
||||
> entschlüsseln - der Schlüssel wurde nach dem Speichern getauscht. Die alte App
|
||||
> kann sie damit ebenso wenig lesen, es ging also nichts verloren. Die
|
||||
> Session-Cookies liegen im Klartext und wurden uebernommen; damit scrapt die
|
||||
> Session-Cookies liegen im Klartext und wurden übernommen; damit scrapt die
|
||||
> neue Instanz sofort.
|
||||
|
||||
## Oberflaeche
|
||||
## Oberfläche
|
||||
|
||||
| Reiter | Datei | Inhalt |
|
||||
| --- | --- | --- |
|
||||
| Dashboard | `ScannerDashboard.tsx` | Kennzahlen, Filter, Trefferliste, *Jetzt scannen*, Ton bei neuen Treffern |
|
||||
| Unpassend | `ScannerRejected.tsx` | Treffer mit Status `rejected` |
|
||||
| Suchagenten | `ScannerAgents.tsx` | Beraterprofile, CV-Upload |
|
||||
| Boersen-Konten | `ScannerPortals.tsx` | Verbindungsstatus, Zugangsdaten, Cookie-Import |
|
||||
| Börsen-Konten | `ScannerPortals.tsx` | Verbindungsstatus, Zugangsdaten, Cookie-Import |
|
||||
| Einstellungen | `settings/ScannerSettings.tsx` | Intervall, Schwellenwerte, OpenAI, Ton, E-Mail |
|
||||
|
||||
**Wichtig zum Ton:** Er haengt an der Dashboard-Komponente. Wer auf einen anderen
|
||||
Menuepunkt wechselt, hoert nichts - die Komponente wird ausgebaut und die
|
||||
**Wichtig zum Ton:** Er hängt an der Dashboard-Komponente. Wer auf einen anderen
|
||||
Menüpunkt wechselt, hört nichts - die Komponente wird ausgebaut und die
|
||||
30-Sekunden-Abfrage endet. In einem anderen Browsertab bleibt der Ton dagegen
|
||||
aktiv, nur langsamer (Chrome bremst Timer versteckter Tabs auf etwa einmal pro
|
||||
Minute) und nur, wenn zuvor irgendwo auf der Seite geklickt wurde
|
||||
(Autoplay-Regel). Browserunabhaengig meldet nur die E-Mail.
|
||||
(Autoplay-Regel). Browserunabhängig meldet nur die E-Mail.
|
||||
|
||||
## Endpunkte
|
||||
|
||||
@@ -187,15 +187,15 @@ Alle unter `/api/scanner`, alle hinter `requireAuth`:
|
||||
| Methode | Pfad | Zweck |
|
||||
| --- | --- | --- |
|
||||
| GET | `/agents` | Liste ohne `cvText` (Nutzlast) |
|
||||
| POST/PUT | `/agents[/:id]` | anlegen/aendern, `multipart` mit CV |
|
||||
| POST/PUT | `/agents[/:id]` | anlegen/ändern, `multipart` mit CV |
|
||||
| DELETE | `/agents/:id` | Agent samt CV-Datei |
|
||||
| GET | `/agents/:id/cv` | PDF als Stream |
|
||||
| GET | `/portals` | Status ohne Geheimnisse |
|
||||
| POST | `/portals/credentials` | Zugangsdaten, AES-verschluesselt |
|
||||
| POST | `/portals/sync-session` | Cookie-Import, normalisiert fuer Playwright |
|
||||
| POST | `/portals/test-connection/:id` | Mini-Scrape bzw. Session-Pruefung |
|
||||
| POST | `/portals/credentials` | Zugangsdaten, AES-verschlüsselt |
|
||||
| POST | `/portals/sync-session` | Cookie-Import, normalisiert für Playwright |
|
||||
| POST | `/portals/test-connection/:id` | Mini-Scrape bzw. Session-Prüfung |
|
||||
| GET | `/matches` | Filter: `status`, `agentId`, `minPercentage`, `limit` |
|
||||
| PUT/DELETE | `/matches/:id` | Status setzen, loeschen |
|
||||
| PUT/DELETE | `/matches/:id` | Status setzen, löschen |
|
||||
| GET/POST | `/settings` | Geheimnisse maskiert |
|
||||
| POST | `/scan` | Lauf starten, `409` wenn schon einer laeuft |
|
||||
| GET | `/status` | laeuft ein Lauf, Scheduler an, Datenbankname |
|
||||
| POST | `/scan` | Lauf starten, `409` wenn schon einer läuft |
|
||||
| GET | `/status` | läuft ein Lauf, Scheduler an, Datenbankname |
|
||||
|
||||
Reference in New Issue
Block a user