update add report

This commit is contained in:
2026-02-17 09:53:13 -06:00
parent df1be3b823
commit eb19b2785e
6 changed files with 158 additions and 16 deletions

View File

@@ -1,8 +1,11 @@
// qbo_helper.js
require('dotenv').config();
const OAuthClient = require('intuit-oauth');
const fs = require('fs');
const path = require('path');
let oauthClient = null;
const tokenFile = path.join(__dirname, 'qbo_token.json');
const getOAuthClient = () => {
if (!oauthClient) {
@@ -10,27 +13,55 @@ const getOAuthClient = () => {
clientId: process.env.QBO_CLIENT_ID,
clientSecret: process.env.QBO_CLIENT_SECRET,
environment: process.env.QBO_ENVIRONMENT || 'sandbox',
redirectUri: process.env.QBO_REDIRECT_URI,
token: {
redirectUri: process.env.QBO_REDIRECT_URI
});
let savedToken = null;
try {
if (fs.existsSync(tokenFile)) {
savedToken = JSON.parse(fs.readFileSync(tokenFile, 'utf8'));
}
} catch (e) {
console.error("❌ Fehler beim Laden des gespeicherten Tokens:", e);
}
if (savedToken) {
oauthClient.setToken(savedToken);
console.log("✅ Gespeicherter Token geladen.");
} else {
// Fallback auf .env (initiale Tokens)
const envToken = {
access_token: process.env.QBO_ACCESS_TOKEN || '',
refresh_token: process.env.QBO_REFRESH_TOKEN || '',
realmId: process.env.QBO_REALM_ID
}
});
};
oauthClient.setToken(envToken);
console.log(" Token aus .env geladen (Fallback).");
}
}
return oauthClient;
};
function saveTokens() {
try {
const token = getOAuthClient().getToken();
fs.writeFileSync(tokenFile, JSON.stringify(token, null, 2));
console.log("💾 Tokens erfolgreich in qbo_token.json gespeichert.");
} catch (e) {
console.error("❌ Fehler beim Speichern der Tokens:", e);
}
}
async function makeQboApiCall(requestOptions) {
const client = getOAuthClient();
// Funktion zum Aktualisieren des Tokens
const doRefresh = async () => {
console.log("🔄 QBO Token Refresh wird ausgeführt (401 Error gefangen)...");
console.log("🔄 QBO Token Refresh wird ausgeführt...");
try {
const authResponse = await client.refresh();
console.log("✅ Token erfolgreich erneuert.");
// Hier müsste man idealerweise die neuen Tokens speichern
saveTokens(); // Neue Tokens persistent speichern
return authResponse;
} catch (e) {
console.error("❌ Refresh fehlgeschlagen:", e.originalMessage || e);
@@ -38,34 +69,35 @@ async function makeQboApiCall(requestOptions) {
}
};
// --- ÄNDERUNG: KEINE VORAB-PRÜFUNG MEHR ---
// Wir vertrauen darauf, dass der Token in der .env aktuell ist (da du ihn gerade generiert hast).
// Wir entfernen client.isAccessTokenValid(), da dies oft falsch negativ ist nach Neustart.
// Vorab-Prüfung: Wenn Token ungültig (basierend auf expires_at), refreshen
if (!client.isAccessTokenValid()) {
console.log("⚠️ Access Token ist ungültig oder abgelaufen. Refresh wird durchgeführt.");
await doRefresh();
}
try {
// Versuch 1: Einfach machen!
// API-Aufruf durchführen
const response = await client.makeApiCall(requestOptions);
// Prüfen, ob QBO eine Fehlermeldung im Body sendet (trotz HTTP 200/400)
// Prüfen, ob QBO eine Fehlermeldung im Body sendet
const data = response.getJson ? response.getJson() : response.json;
if (data.fault && data.fault.error) {
const errorCode = data.fault.error[0].code;
// Fehler 3202 = Missing Access Token / Invalid
// Manchmal sendet QBO auch 401 im Body
if (errorCode === '3202' || errorCode === '3100') {
console.log(`⚠️ QBO meldet Token-Fehler (${errorCode}). Versuche Refresh und Retry...`);
await doRefresh();
return await client.makeApiCall(requestOptions);
}
// Anderen API-Fehler werfen (z.B. Validierung)
throw new Error(`QBO API Error ${errorCode}: ${data.fault.error[0].message}`);
}
// Erfolgreichen Aufruf: Tokens speichern (falls geändert)
saveTokens();
return response;
} catch (e) {
// HTTP 401 Unauthorized fangen -> Das ist der ECHTE Indikator, dass der Token abgelaufen ist
// HTTP 401 Unauthorized fangen
const isAuthError = e.response?.status === 401 || (e.authResponse && e.authResponse.response && e.authResponse.response.status === 401);
if (isAuthError) {