Gegenbuch: Dienstkonto statt Token, .env mit gueltigen Werten
Blocker behoben: Ich hatte ein dauerhaftes API-Token vorausgesetzt - das gibt es in OpenCRM nicht. Zugangstoken leben 15 Minuten, der Gegenbuch-Container waere nach dem ersten Durchlauf gestorben. Aufgefallen erst durch die Frage des Betreibers, woher er den Token nimmt. Loesung: Das Gegenbuch meldet sich bei jedem Lauf selbst an, mit einem eigenen Benutzerkonto, dessen Rolle ausschliesslich audit:read traegt. Damit kann es nur Pruefwerte lesen - keine Kundendaten, keine Aenderungen. CRM_TOKEN bleibt fuer Tests moeglich, ist aber nicht mehr der Normalweg. Fehlerfaelle (falsches Passwort, Anmelde-Bremse, CRM nicht erreichbar) werden unterschieden und im Klartext gemeldet. .env.example nennt jetzt zu jedem Schalter die gueltigen Werte - bisher liess sich nur raten, ob es prod oder production heisst. Einschliesslich des Falls "erst nur Staging testen, Prod spaeter dazunehmen". Beide READMEs um "Zugang einrichten" ergaenzt: Rolle mit nur audit:read, Benutzer damit, Zugangsdaten in die .env. Mit dem Hinweis, dass jede Anmeldung im Audit-Log erscheint - gewollt, denn so faellt auch auf, wenn das Gegenbuch aufhoert zu arbeiten. Verifiziert gegen eine Attrappe, die wie das echte CRM eine Anmeldung verlangt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -466,7 +466,7 @@ beim nächsten Durchlauf auf.
|
|||||||
Die Richtung ist dabei entscheidend:
|
Die Richtung ist dabei entscheidend:
|
||||||
|
|
||||||
```
|
```
|
||||||
Gegenbuch ──holt lesend──> OpenCRM (HTTPS, Token nur mit audit:read)
|
Gegenbuch ──holt lesend──> OpenCRM (HTTPS, Konto nur mit audit:read)
|
||||||
OpenCRM ─────────────────> (kennt das Gegenbuch nicht)
|
OpenCRM ─────────────────> (kennt das Gegenbuch nicht)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -479,10 +479,15 @@ keine Geheimnisse enthält.
|
|||||||
```bash
|
```bash
|
||||||
git clone <dieses Repository> opencrm
|
git clone <dieses Repository> opencrm
|
||||||
cd opencrm/tools/audit-notary
|
cd opencrm/tools/audit-notary
|
||||||
cp .env.example .env # CRM-Adresse und Token eintragen
|
cp .env.example .env # CRM-Adresse und Dienstkonto eintragen
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Vorher im CRM anlegen:** eine Rolle mit ausschließlich dem Recht
|
||||||
|
`audit:read` und einen Benutzer damit. Das Gegenbuch meldet sich mit diesem
|
||||||
|
Konto bei jedem Durchlauf selbst an – ein dauerhaftes Token gibt es nicht, weil
|
||||||
|
Zugangstoken nach 15 Minuten ablaufen.
|
||||||
|
|
||||||
Zwei Bücher auf einer Maschine – etwa für Produktion und Test – sind
|
Zwei Bücher auf einer Maschine – etwa für Produktion und Test – sind
|
||||||
vorgesehen. Alles Weitere in
|
vorgesehen. Alles Weitere in
|
||||||
[tools/audit-notary/README.md](tools/audit-notary/README.md).
|
[tools/audit-notary/README.md](tools/audit-notary/README.md).
|
||||||
|
|||||||
@@ -97,6 +97,30 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
|
|||||||
|
|
||||||
## ✅ Erledigt
|
## ✅ Erledigt
|
||||||
|
|
||||||
|
- [x] **🔑 Gegenbuch: Dienstkonto statt Token, .env mit gueltigen Werten** (2026-08-22)
|
||||||
|
- **Blocker gefunden und behoben:** Ich hatte ein dauerhaftes API-Token
|
||||||
|
vorausgesetzt – das gibt es in OpenCRM gar nicht. Zugangstoken leben
|
||||||
|
**15 Minuten**; der Gegenbuch-Container waere nach dem ersten Durchlauf
|
||||||
|
gestorben. Aufgefallen erst, als der Betreiber fragte, woher er den Token
|
||||||
|
nimmt.
|
||||||
|
Loesung: Das Gegenbuch meldet sich bei **jedem Lauf selbst an** – mit einem
|
||||||
|
eigenen Benutzerkonto, dessen Rolle ausschliesslich `audit:read` traegt.
|
||||||
|
Damit kann es nur Pruefwerte lesen: keine Kundendaten, keine Aenderungen.
|
||||||
|
`CRM_TOKEN` bleibt fuer Tests moeglich, ist aber nicht mehr der Normalweg.
|
||||||
|
- Fehlerfaelle sauber gemeldet: falsches Passwort → Klartext statt HTTP-Code,
|
||||||
|
Anmelde-Bremse (429) benannt, CRM nicht erreichbar unterschieden.
|
||||||
|
- **`.env.example` nennt jetzt die gueltigen Werte** – bisher liess sich nur
|
||||||
|
raten, ob es `prod` oder `production` heisst. Fuer jeden Schalter steht
|
||||||
|
dabei, was erlaubt ist, inkl. des Falls „erst nur Staging testen, Prod
|
||||||
|
spaeter dazunehmen“ (`COMPOSE_PROFILES=staging` → `prod,staging`).
|
||||||
|
- Beide READMEs um „Zugang einrichten“ ergaenzt: Rolle mit nur `audit:read`,
|
||||||
|
Benutzer damit, Zugangsdaten in die `.env`. Mit dem Hinweis, dass jede
|
||||||
|
Anmeldung im Audit-Log erscheint – gewollt, denn so sieht man auch, wenn
|
||||||
|
das Gegenbuch aufhoert zu arbeiten.
|
||||||
|
- Verifiziert gegen eine Attrappe, die wie das echte CRM eine Anmeldung
|
||||||
|
verlangt: Anmeldung + Lauf erfolgreich, falsches Passwort → verstaendliche
|
||||||
|
Meldung, exit 1.
|
||||||
|
|
||||||
- [x] **🐳 Gegenbuch als Docker-Setup, lokales Buch auf eigener Maschine** (2026-08-22)
|
- [x] **🐳 Gegenbuch als Docker-Setup, lokales Buch auf eigener Maschine** (2026-08-22)
|
||||||
- Betreiber-Entscheidung: Das Gegenbuch laeuft auf einer **eigenen Maschine**
|
- Betreiber-Entscheidung: Das Gegenbuch laeuft auf einer **eigenen Maschine**
|
||||||
fuer Prod und Staging; ein externes Git-Repository entfaellt, das Buch
|
fuer Prod und Staging; ein externes Git-Repository entfaellt, das Buch
|
||||||
|
|||||||
@@ -6,41 +6,68 @@
|
|||||||
# Was hier passiert: Der Rechner holt regelmäßig einen kurzen Kontrollwert von
|
# Was hier passiert: Der Rechner holt regelmäßig einen kurzen Kontrollwert von
|
||||||
# OpenCRM ab und schreibt ihn in ein Buch, das nur hier liegt. Wird später im
|
# OpenCRM ab und schreibt ihn in ein Buch, das nur hier liegt. Wird später im
|
||||||
# CRM etwas nachträglich verändert, widerspricht das dem Buch.
|
# CRM etwas nachträglich verändert, widerspricht das dem Buch.
|
||||||
#
|
|
||||||
# Welche Bücher sollen laufen?
|
|
||||||
COMPOSE_PROFILES=prod,staging
|
|
||||||
|
|
||||||
# Nur Kosmetik – steht in den Einträgen des Buchs.
|
|
||||||
|
# ============================================================
|
||||||
|
# Welche Bücher sollen laufen?
|
||||||
|
# ============================================================
|
||||||
|
# Gültige Werte: prod | staging | prod,staging | (leer = keins)
|
||||||
|
# Genau so geschrieben – nicht "production" oder "test".
|
||||||
|
#
|
||||||
|
# Nur Staging testen: COMPOSE_PROFILES=staging
|
||||||
|
# Später Prod dazunehmen: COMPOSE_PROFILES=prod,staging
|
||||||
|
# Danach: docker compose up -d (der laufende Dienst bleibt unberührt)
|
||||||
|
COMPOSE_PROFILES=staging
|
||||||
|
|
||||||
|
# Steht in den Einträgen des Buchs. Beliebiger Text, nur Kosmetik.
|
||||||
NOTAR_EMAIL=gegenbuch@example.de
|
NOTAR_EMAIL=gegenbuch@example.de
|
||||||
|
|
||||||
# ---------------- Produktion ----------------
|
|
||||||
# Von welcher OpenCRM-Instanz wird geholt?
|
# ============================================================
|
||||||
|
# Produktion
|
||||||
|
# ============================================================
|
||||||
|
# Adresse der OpenCRM-Instanz, von der geholt wird.
|
||||||
|
# Gültig: vollständige URL mit https:// und OHNE Schrägstrich am Ende.
|
||||||
PROD_CRM_URL=https://crm.example.de
|
PROD_CRM_URL=https://crm.example.de
|
||||||
|
|
||||||
# Token eines Benutzers mit dem Recht audit:read – MEHR NICHT.
|
# Zugang: ein eigenes Benutzerkonto im CRM, das NUR das Recht "audit:read" hat.
|
||||||
# Damit lassen sich ausschließlich Prüfwerte lesen: keine Kundendaten, keine
|
# Wie man es anlegt, steht in der README unter "Zugang einrichten".
|
||||||
# Änderungen. Selbst wenn es abhandenkommt, ist damit nichts anzufangen.
|
#
|
||||||
PROD_CRM_TOKEN=
|
# Das Gegenbuch meldet sich damit bei jedem Durchlauf selbst an. Ein fest
|
||||||
|
# hinterlegtes Token gibt es bewusst nicht – Zugangstoken laufen nach
|
||||||
|
# 15 Minuten ab und wären beim nächsten Durchlauf längst ungültig.
|
||||||
|
PROD_CRM_EMAIL=gegenbuch@deine-domain.de
|
||||||
|
PROD_CRM_PASSWORD=
|
||||||
|
|
||||||
# Wie oft geprüft wird (Sekunden). 3600 = stündlich.
|
# Wie oft geprüft wird, in Sekunden.
|
||||||
|
# Gültig: ganze Zahl > 0. Üblich: 3600 (stündlich), 900 (viertelstündlich)
|
||||||
# Kürzer heißt: kleineres Zeitfenster, in dem eine Änderung unbemerkt bliebe.
|
# Kürzer heißt: kleineres Zeitfenster, in dem eine Änderung unbemerkt bliebe.
|
||||||
PROD_INTERVAL=3600
|
PROD_INTERVAL=3600
|
||||||
|
|
||||||
# Beim ALLERERSTEN Start einmalig auf true setzen, danach wieder leeren.
|
# Beim ALLERERSTEN Start einmalig setzen, danach wieder leeren.
|
||||||
|
# Gültige Werte: true | (leer)
|
||||||
# Grund: Die erste Eintragung legt fest, was als Ausgangszustand gilt – das
|
# Grund: Die erste Eintragung legt fest, was als Ausgangszustand gilt – das
|
||||||
# soll nicht versehentlich passieren.
|
# soll nicht versehentlich passieren.
|
||||||
PROD_GENESIS_ACK=
|
PROD_GENESIS_ACK=
|
||||||
|
|
||||||
# Normalerweise leer lassen. Nur nötig, wenn das Gedächtnis des Gegenbuchs
|
# Normalerweise leer lassen.
|
||||||
# verlorenging (z. B. Verzeichnis gelöscht) und du geklärt hast, warum.
|
# Gültige Werte: true | (leer)
|
||||||
|
# Nur nötig, wenn das Datenverzeichnis verlorenging (z. B. gelöscht) UND du
|
||||||
|
# geklärt hast, warum. Siehe README, Abschnitt "Wenn das Gedächtnis fehlt".
|
||||||
PROD_ADOPT_ACK=
|
PROD_ADOPT_ACK=
|
||||||
|
|
||||||
# Wo das Buch liegt. DIESES VERZEICHNIS GEHÖRT INS BACKUP.
|
# Wo das Buch liegt – relativ zu diesem Verzeichnis.
|
||||||
|
# DIESES VERZEICHNIS GEHÖRT INS BACKUP (enthält Buch und Signaturschlüssel).
|
||||||
PROD_DIR=./data/prod
|
PROD_DIR=./data/prod
|
||||||
|
|
||||||
# ---------------- Test / Staging ----------------
|
|
||||||
|
# ============================================================
|
||||||
|
# Test / Staging
|
||||||
|
# ============================================================
|
||||||
|
# Gleiche Regeln wie oben, eigenes Konto und eigenes Verzeichnis.
|
||||||
STAGING_CRM_URL=https://staging.example.de
|
STAGING_CRM_URL=https://staging.example.de
|
||||||
STAGING_CRM_TOKEN=
|
STAGING_CRM_EMAIL=gegenbuch@deine-domain.de
|
||||||
|
STAGING_CRM_PASSWORD=
|
||||||
STAGING_INTERVAL=3600
|
STAGING_INTERVAL=3600
|
||||||
STAGING_GENESIS_ACK=
|
STAGING_GENESIS_ACK=
|
||||||
STAGING_ADOPT_ACK=
|
STAGING_ADOPT_ACK=
|
||||||
|
|||||||
@@ -57,10 +57,36 @@ soll nicht versehentlich passieren.
|
|||||||
getrennte Dienste mit getrennten Verzeichnissen und getrennten Schlüsseln.
|
getrennte Dienste mit getrennten Verzeichnissen und getrennten Schlüsseln.
|
||||||
Welche laufen, steuert `COMPOSE_PROFILES` in der `.env`.
|
Welche laufen, steuert `COMPOSE_PROFILES` in der `.env`.
|
||||||
|
|
||||||
|
### Zugang einrichten (das brauchst du vorher)
|
||||||
|
|
||||||
|
Das Gegenbuch braucht ein **eigenes Benutzerkonto** im CRM – kein Token. Der
|
||||||
|
Grund: Zugangstoken laufen nach 15 Minuten ab und wären beim nächsten
|
||||||
|
stündlichen Durchlauf längst ungültig. Das Gegenbuch meldet sich deshalb bei
|
||||||
|
jedem Lauf selbst an.
|
||||||
|
|
||||||
|
Im CRM, als Administrator:
|
||||||
|
|
||||||
|
1. **Rolle anlegen**, z. B. `Gegenbuch` – und ihr **ausschließlich** das Recht
|
||||||
|
`audit:read` geben. Sonst nichts.
|
||||||
|
2. **Benutzer anlegen**, z. B. `gegenbuch@deine-domain.de`, mit dieser Rolle
|
||||||
|
und einem langen, zufälligen Passwort.
|
||||||
|
3. E-Mail und Passwort in die `.env` des Gegenbuchs eintragen
|
||||||
|
(`PROD_CRM_EMAIL` / `PROD_CRM_PASSWORD`).
|
||||||
|
|
||||||
|
Mit `audit:read` allein kann dieses Konto **nur Prüfwerte lesen** – keine
|
||||||
|
Kundendaten, keine Verträge, nichts ändern. Selbst wenn die Zugangsdaten
|
||||||
|
abhandenkommen, ist damit nichts anzufangen.
|
||||||
|
|
||||||
|
Für Produktion und Test jeweils ein eigenes Konto in der jeweiligen Instanz.
|
||||||
|
|
||||||
|
> Jede Anmeldung erscheint im Audit-Log der jeweiligen Instanz. Das ist so
|
||||||
|
> gewollt: Man sieht, dass das Gegenbuch arbeitet – und wenn es aufhört, fällt
|
||||||
|
> auch das auf.
|
||||||
|
|
||||||
### Wer redet mit wem
|
### Wer redet mit wem
|
||||||
|
|
||||||
```
|
```
|
||||||
Gegenbuch ──holt lesend──> OpenCRM (HTTPS, Token nur mit audit:read)
|
Gegenbuch ──holt lesend──> OpenCRM (HTTPS, Konto nur mit audit:read)
|
||||||
OpenCRM ─────────────────> (kennt das Gegenbuch nicht)
|
OpenCRM ─────────────────> (kennt das Gegenbuch nicht)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
INSTANZ: prod
|
INSTANZ: prod
|
||||||
CRM_URL: ${PROD_CRM_URL}
|
CRM_URL: ${PROD_CRM_URL}
|
||||||
CRM_TOKEN: ${PROD_CRM_TOKEN}
|
CRM_EMAIL: ${PROD_CRM_EMAIL}
|
||||||
|
CRM_PASSWORD: ${PROD_CRM_PASSWORD}
|
||||||
NOTARY_INTERVAL: ${PROD_INTERVAL:-3600}
|
NOTARY_INTERVAL: ${PROD_INTERVAL:-3600}
|
||||||
NOTAR_EMAIL: ${NOTAR_EMAIL:-gegenbuch@localhost}
|
NOTAR_EMAIL: ${NOTAR_EMAIL:-gegenbuch@localhost}
|
||||||
# Nur beim allerersten Lauf einmalig auf true, danach wieder leeren:
|
# Nur beim allerersten Lauf einmalig auf true, danach wieder leeren:
|
||||||
@@ -46,7 +47,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
INSTANZ: staging
|
INSTANZ: staging
|
||||||
CRM_URL: ${STAGING_CRM_URL}
|
CRM_URL: ${STAGING_CRM_URL}
|
||||||
CRM_TOKEN: ${STAGING_CRM_TOKEN}
|
CRM_EMAIL: ${STAGING_CRM_EMAIL}
|
||||||
|
CRM_PASSWORD: ${STAGING_CRM_PASSWORD}
|
||||||
NOTARY_INTERVAL: ${STAGING_INTERVAL:-3600}
|
NOTARY_INTERVAL: ${STAGING_INTERVAL:-3600}
|
||||||
NOTAR_EMAIL: ${NOTAR_EMAIL:-gegenbuch@localhost}
|
NOTAR_EMAIL: ${NOTAR_EMAIL:-gegenbuch@localhost}
|
||||||
NOTARY_GENESIS_ACK: ${STAGING_GENESIS_ACK:-}
|
NOTARY_GENESIS_ACK: ${STAGING_GENESIS_ACK:-}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ set -uo pipefail
|
|||||||
|
|
||||||
: "${INSTANZ:?INSTANZ fehlt (z. B. prod oder staging)}"
|
: "${INSTANZ:?INSTANZ fehlt (z. B. prod oder staging)}"
|
||||||
: "${CRM_URL:?CRM_URL fehlt – von welcher OpenCRM-Instanz soll geholt werden?}"
|
: "${CRM_URL:?CRM_URL fehlt – von welcher OpenCRM-Instanz soll geholt werden?}"
|
||||||
: "${CRM_TOKEN:?CRM_TOKEN fehlt – Token eines Benutzers mit audit:read}"
|
: "${CRM_EMAIL:?CRM_EMAIL fehlt – Dienstkonto im CRM mit dem Recht audit:read}"
|
||||||
|
: "${CRM_PASSWORD:?CRM_PASSWORD fehlt – Passwort dieses Dienstkontos}"
|
||||||
|
|
||||||
INTERVALL="${NOTARY_INTERVAL:-3600}"
|
INTERVALL="${NOTARY_INTERVAL:-3600}"
|
||||||
BUCH=/gegenbuch/buch
|
BUCH=/gegenbuch/buch
|
||||||
|
|||||||
@@ -132,8 +132,20 @@ if (!SIGN && process.env.NOTARY_INSECURE_ACK !== 'mir-ist-klar-dass-das-ungeschu
|
|||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
if (!CRM_URL || !CRM_TOKEN) {
|
const CRM_EMAIL = process.env.CRM_EMAIL;
|
||||||
console.error('CRM_URL und CRM_TOKEN müssen gesetzt sein.');
|
const CRM_PASSWORD = process.env.CRM_PASSWORD;
|
||||||
|
|
||||||
|
if (!CRM_URL) {
|
||||||
|
console.error('CRM_URL muss gesetzt sein.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (!CRM_TOKEN && !(CRM_EMAIL && CRM_PASSWORD)) {
|
||||||
|
console.error(
|
||||||
|
'Es fehlt der Zugang zum CRM.\n' +
|
||||||
|
'Entweder CRM_EMAIL und CRM_PASSWORD eines Dienstkontos setzen (empfohlen –\n' +
|
||||||
|
'das Gegenbuch meldet sich dann bei jedem Lauf selbst an), oder ein bereits\n' +
|
||||||
|
'vorhandenes CRM_TOKEN mitgeben (läuft nach 15 Minuten ab, nur für Tests).',
|
||||||
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,13 +192,56 @@ if (SIGN && !PIN) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Access-Tokens leben nur 15 Minuten – ein dauerhaft hinterlegtes Token waere
|
||||||
|
// beim naechsten stuendlichen Lauf laengst abgelaufen. Deshalb meldet sich das
|
||||||
|
// Gegenbuch mit einem eigenen Dienstkonto an und holt sich pro Lauf ein
|
||||||
|
// frisches. Das Konto braucht ausschliesslich die Berechtigung `audit:read`.
|
||||||
|
let zugangsToken = CRM_TOKEN || null;
|
||||||
|
|
||||||
|
async function anmelden() {
|
||||||
|
let r;
|
||||||
|
try {
|
||||||
|
r = await fetch(`${CRM_URL}/api/auth/login`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'content-type': 'application/json' },
|
||||||
|
body: JSON.stringify({ email: CRM_EMAIL, password: CRM_PASSWORD }),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
`Das CRM ist nicht erreichbar (${CRM_URL}).\n` +
|
||||||
|
` Grund: ${e instanceof Error ? e.message : String(e)}`,
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (r.status === 401) {
|
||||||
|
console.error(
|
||||||
|
'Anmeldung am CRM abgelehnt – E-Mail oder Passwort des Dienstkontos stimmen nicht.',
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (r.status === 429) {
|
||||||
|
console.error('Das CRM hat die Anmeldung wegen zu vieler Versuche gebremst. Später erneut.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (!r.ok) {
|
||||||
|
console.error(`Anmeldung am CRM fehlgeschlagen: HTTP ${r.status}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const j = await r.json();
|
||||||
|
if (!j.success || !j.data?.token) {
|
||||||
|
console.error(`Anmeldung am CRM fehlgeschlagen: ${j.error || 'kein Token in der Antwort'}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
zugangsToken = j.data.token;
|
||||||
|
}
|
||||||
|
|
||||||
async function hole(pfad) {
|
async function hole(pfad) {
|
||||||
// Fehler werden hier zu einer erklaerenden Zeile – frueher flog ein
|
// Fehler werden hier zu einer erklaerenden Zeile – frueher flog ein
|
||||||
// Node-Stacktrace hoch, also genau die kryptische erste Zeile, die fuer
|
// Node-Stacktrace hoch, also genau die kryptische erste Zeile, die fuer
|
||||||
// git-Meldungen schon abgestellt war.
|
// git-Meldungen schon abgestellt war.
|
||||||
let r;
|
let r;
|
||||||
try {
|
try {
|
||||||
r = await fetch(`${CRM_URL}${pfad}`, { headers: { Authorization: `Bearer ${CRM_TOKEN}` } });
|
r = await fetch(`${CRM_URL}${pfad}`, { headers: { Authorization: `Bearer ${zugangsToken}` } });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(
|
console.error(
|
||||||
`Das CRM ist nicht erreichbar (${CRM_URL}).\n` +
|
`Das CRM ist nicht erreichbar (${CRM_URL}).\n` +
|
||||||
@@ -672,6 +727,7 @@ if (Number.isFinite(MIN_SEQ) && bisher.length < MIN_SEQ) {
|
|||||||
// 4) Abgleich mit dem CRM, VOR dem Anhaengen.
|
// 4) Abgleich mit dem CRM, VOR dem Anhaengen.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
const letzter = bisher[bisher.length - 1];
|
const letzter = bisher[bisher.length - 1];
|
||||||
|
if (!CRM_TOKEN) await anmelden();
|
||||||
const aktuell = await hole('/api/audit-logs/checkpoint');
|
const aktuell = await hole('/api/audit-logs/checkpoint');
|
||||||
|
|
||||||
if (letzter) {
|
if (letzter) {
|
||||||
|
|||||||
Reference in New Issue
Block a user