"Update API configuration and SSL certificates for enhanced security"

This commit is contained in:
2025-05-26 09:54:29 +02:00
parent 45fcc1a948
commit 201f75cfd3
9 changed files with 8308 additions and 84 deletions

8081
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -71,7 +71,7 @@
"@tailwindcss/forms": "^0.5.9",
"@types/lodash": "^4.17.13",
"@types/luxon": "^3.4.2",
"@types/node": "^20.16.11",
"@types/node": "^20.17.50",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"drizzle-kit": "^0.21.4",

View File

@@ -1,5 +1,46 @@
// Basis-URL für Backend-API
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "https://192.168.0.105:5000";
// Versucht verschiedene Verbindungsoptionen mit Fallbacks
const getApiBaseUrl = () => {
// Explizit gesetzte Umgebungsvariable hat höchste Priorität
if (process.env.NEXT_PUBLIC_API_URL) {
return process.env.NEXT_PUBLIC_API_URL;
}
// Im Browser: Verschiedene Verbindungsoptionen versuchen
if (typeof window !== 'undefined') {
// Primäre Verbindungsoption: HTTPS auf Port 443
const hostname = window.location.hostname === 'localhost' ? 'raspberrypi' : window.location.hostname;
// Verbindungsoptionen in Prioritätsreihenfolge
return {
primary: `https://${hostname}:443`,
fallbacks: [
`http://${hostname}:443`,
`https://${hostname}:80`,
`http://${hostname}:80`,
`https://${hostname}:5000`,
`http://${hostname}:5000`,
`https://raspberrypi:443`,
`http://raspberrypi:443`,
`https://raspberrypi:80`,
`http://raspberrypi:80`,
`https://raspberrypi:5000`,
`http://raspberrypi:5000`,
`https://192.168.0.105:443`,
`http://192.168.0.105:443`,
`https://192.168.0.105:80`,
`http://192.168.0.105:80`,
`https://192.168.0.105:5000`,
`http://192.168.0.105:5000`,
]
};
}
// Standardwert für serverseitiges Rendering
return `https://raspberrypi:443`;
};
export const API_BASE_URL = getApiBaseUrl();
// Frontend-URL für Callbacks - unterstützt mehrere Domains
const getFrontendUrl = () => {
@@ -40,14 +81,45 @@ export const ALLOWED_CALLBACK_HOSTS = [
// Funktion zum Testen der SSL-Verbindung
export const testSSLConnection = async (): Promise<boolean> => {
try {
// Führe einen einfachen GET-Request zum Backend aus
const response = await fetch(`${API_BASE_URL}/health`, {
method: 'GET',
mode: 'no-cors', // Keine CORS-Fehler erzeugen
});
// Wenn kein Fehler auftritt, ist die SSL-Verbindung erfolgreich
return true;
// Bei mehreren Verbindungsoptionen alle testen
if (typeof API_BASE_URL === 'object' && API_BASE_URL.primary && API_BASE_URL.fallbacks) {
// Zuerst primäre Verbindung testen
try {
const response = await fetch(`${API_BASE_URL.primary}/health`, {
method: 'GET',
mode: 'no-cors',
});
// Wenn kein Fehler auftritt, ist die primäre Verbindung erfolgreich
return true;
} catch (primaryError) {
console.warn('Primäre SSL-Verbindung fehlgeschlagen, versuche Fallbacks:', primaryError);
// Fallbacks durchprobieren
for (const fallbackUrl of API_BASE_URL.fallbacks) {
try {
const response = await fetch(`${fallbackUrl}/health`, {
method: 'GET',
mode: 'no-cors',
});
// Wenn kein Fehler auftritt, ist die Fallback-Verbindung erfolgreich
return true;
} catch (fallbackError) {
// Fehlgeschlagenen Fallback ignorieren und nächsten versuchen
console.warn(`Fallback ${fallbackUrl} fehlgeschlagen:`, fallbackError);
}
}
// Alle Fallbacks fehlgeschlagen
return false;
}
} else {
// Einfacher Verbindungstest für String-URL
const response = await fetch(`${API_BASE_URL}/health`, {
method: 'GET',
mode: 'no-cors',
});
return true;
}
} catch (error) {
console.warn('SSL-Verbindungstest fehlgeschlagen:', error);
return false;
@@ -56,13 +128,13 @@ export const testSSLConnection = async (): Promise<boolean> => {
// Endpunkte für die verschiedenen Ressourcen
export const API_ENDPOINTS = {
PRINTERS: `${API_BASE_URL}/api/printers`,
JOBS: `${API_BASE_URL}/api/jobs`,
USERS: `${API_BASE_URL}/api/users`,
PRINTERS: `/api/printers`,
JOBS: `/api/jobs`,
USERS: `/api/users`,
// OAuth-spezifische Endpunkte
AUTH: {
LOGIN: `${API_BASE_URL}/api/auth/login`,
CALLBACK: `${API_BASE_URL}/api/auth/callback`,
LOGIN: `/api/auth/login`,
CALLBACK: `/api/auth/callback`,
}
};

View File

@@ -5,19 +5,68 @@ import { API_BASE_URL } from './api-config';
* Enthält Logik für Offline-Fallback und Behandlung von selbstsignierten Zertifikaten
*/
export class ExternalAPI {
private baseURL: string;
private baseURL: string | { primary: string; fallbacks: string[] };
private activeURL: string | null = null;
constructor() {
this.baseURL = API_BASE_URL;
// Wenn das API_BASE_URL ein Objekt ist, setzen wir die primäre URL als aktiv
if (typeof this.baseURL === 'object' && this.baseURL.primary) {
this.activeURL = this.baseURL.primary;
} else if (typeof this.baseURL === 'string') {
this.activeURL = this.baseURL;
}
}
/**
* Führt einen API-Request durch mit Unterstützung für selbstsignierte Zertifikate
* im Entwicklungsmodus
* und Fallback-URLs wenn die primäre Verbindung fehlschlägt
*/
async fetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const url = `${this.baseURL}${endpoint}`;
// Wenn wir ein Objekt mit mehreren URLs haben
if (typeof this.baseURL === 'object' && this.baseURL.primary && this.baseURL.fallbacks) {
// Versuche zuerst die aktive URL (primär oder eine der Fallbacks)
if (this.activeURL) {
try {
const response = await this.fetchFromUrl<T>(this.activeURL + endpoint, options);
// Wenn erfolgreich, behalte diese URL bei
return response;
} catch (error) {
console.warn(`Verbindung zu ${this.activeURL} fehlgeschlagen, versuche Fallbacks...`);
// Wenn primäre URL fehlschlägt, versuche alle Fallbacks
const allUrls = [this.baseURL.primary, ...this.baseURL.fallbacks];
// Versuche alle URLs nacheinander (außer der bereits fehlgeschlagenen aktiven URL)
for (const url of allUrls) {
if (url === this.activeURL) continue; // Überspringe die bereits versuchte URL
try {
const response = await this.fetchFromUrl<T>(url + endpoint, options);
// Wenn erfolgreich, setze diese URL als neue aktive URL
this.activeURL = url;
console.log(`Verbindung zu ${url} erfolgreich hergestellt.`);
return response;
} catch (fallbackError) {
// Fehlgeschlagenen Fallback ignorieren und nächsten versuchen
console.warn(`Fallback ${url} fehlgeschlagen...`);
}
}
// Wenn alle Versuche fehlschlagen, wirf den ursprünglichen Fehler
throw new Error(`Keine Verbindung zum Backend möglich. Alle Verbindungsversuche fehlgeschlagen.`);
}
}
}
// Fallback für einfache String-URL
return this.fetchFromUrl<T>((this.activeURL || this.baseURL as string) + endpoint, options);
}
/**
* Führt einen Fetch-Aufruf an einer bestimmten URL durch
*/
private async fetchFromUrl<T>(url: string, options: RequestInit = {}): Promise<T> {
try {
const response = await fetch(url, {
...options,
@@ -25,8 +74,6 @@ export class ExternalAPI {
'Content-Type': 'application/json',
...options.headers,
},
// Im Browser werden selbstsignierte Zertifikate über die Browser-Einstellungen akzeptiert
// Die fetch API im Browser hat keine Option zum Ignorieren von Zertifikaten
});
if (!response.ok) {
@@ -35,11 +82,11 @@ export class ExternalAPI {
return await response.json();
} catch (error) {
console.error('API Fehler:', error);
console.error(`API Fehler (${url}):`, error);
// Prüfen auf Zertifikatsfehler
if (error instanceof Error && error.message.includes('certificate')) {
console.warn('Zertifikatsfehler: Bitte akzeptieren Sie das selbstsignierte Zertifikat manuell, indem Sie direkt auf https://192.168.0.105:5000 zugreifen und es bestätigen.');
console.warn(`Zertifikatsfehler bei ${url}: Bitte akzeptieren Sie das selbstsignierte Zertifikat manuell.`);
}
throw error;