feat: Implement SSL support and kiosk mode enhancements

- Added SSL configuration to the backend, including self-signed certificate generation and management.
- Updated `setup_myp.sh` to create SSL certificates during installation.
- Enhanced `app.py` to support SSL context for secure communication.
- Introduced a new SSL management menu in the setup script for easier certificate handling.
- Updated frontend API calls to use HTTPS for secure data transmission.
- Implemented kiosk mode features, including automatic browser launch with SSL support.
- Improved documentation in `SUMMARY.md` to reflect new features and network topology changes.
This commit is contained in:
2025-05-25 20:59:13 +02:00
parent af3761707a
commit 0d5b87f163
14 changed files with 812 additions and 146 deletions

View File

@@ -1,5 +1,5 @@
// Basis-URL für Backend-API
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://192.168.0.105:5000";
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "https://192.168.0.105:5000";
// Frontend-URL für Callbacks - unterstützt mehrere Domains
const getFrontendUrl = () => {
@@ -15,12 +15,12 @@ const getFrontendUrl = () => {
if (hostname === 'm040tbaraspi001' ||
hostname === 'm040tbaraspi001.de040.corpintra.net' ||
hostname.includes('corpintra.net')) {
return `http://${hostname}`;
return `https://${hostname}`;
}
}
// Priorität 3: Default für Localhost
return "http://localhost:3000";
return "https://localhost:3000";
};
export const FRONTEND_URL = getFrontendUrl();
@@ -37,6 +37,23 @@ export const ALLOWED_CALLBACK_HOSTS = [
'192.168.0.105'
];
// 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;
} catch (error) {
console.warn('SSL-Verbindungstest fehlgeschlagen:', error);
return false;
}
};
// Endpunkte für die verschiedenen Ressourcen
export const API_ENDPOINTS = {
PRINTERS: `${API_BASE_URL}/api/printers`,

View File

@@ -0,0 +1,75 @@
import { ExternalAPI } from './external-api';
// Typdefinitionen für API-Responses
export interface Printer {
id: string;
name: string;
ip: string;
status: string;
is_enabled: boolean;
}
export interface Job {
id: string;
printer_id: string;
user_id: string;
start_time: string;
end_time: string;
status: string;
}
// Instanz der ExternalAPI erstellen
const externalAPI = new ExternalAPI();
// Fetcher für SWR mit Fehlerbehandlung und HTTPS-Unterstützung
const fetchWithErrorHandling = async (endpoint: string) => {
try {
return await externalAPI.fetch(endpoint);
} catch (error) {
console.error('API-Fehler:', error);
throw new Error('Ein Fehler ist bei der API-Anfrage aufgetreten');
}
};
// API-Funktionen, die mit der bisherigen API-Struktur kompatibel sind
export const api = {
// Drucker-Endpunkte
printers: {
getAll: () => fetchWithErrorHandling('/api/printers'),
getById: (id: string) => fetchWithErrorHandling(`/api/printers/${id}`),
create: (data: Partial<Printer>) =>
externalAPI.fetch('/api/printers', {
method: 'POST',
body: JSON.stringify(data),
}),
update: (id: string, data: Partial<Printer>) =>
externalAPI.fetch(`/api/printers/${id}`, {
method: 'PUT',
body: JSON.stringify(data),
}),
delete: (id: string) =>
externalAPI.fetch(`/api/printers/${id}`, {
method: 'DELETE',
}),
},
// Jobs-Endpunkte
jobs: {
getAll: () => fetchWithErrorHandling('/api/jobs'),
getById: (id: string) => fetchWithErrorHandling(`/api/jobs/${id}`),
create: (data: Partial<Job>) =>
externalAPI.fetch('/api/jobs', {
method: 'POST',
body: JSON.stringify(data),
}),
update: (id: string, data: Partial<Job>) =>
externalAPI.fetch(`/api/jobs/${id}`, {
method: 'PUT',
body: JSON.stringify(data),
}),
delete: (id: string) =>
externalAPI.fetch(`/api/jobs/${id}`, {
method: 'DELETE',
}),
},
};

View File

@@ -1,78 +1,57 @@
import { API_ENDPOINTS } from './api-config';
import { API_BASE_URL } from './api-config';
// Typdefinitionen für API-Responses
export interface Printer {
id: string;
name: string;
ip: string;
status: string;
is_enabled: boolean;
}
/**
* ExternalAPI - Wrapper für externe API-Aufrufe mit HTTPS-Unterstützung
* Enthält Logik für Offline-Fallback und Behandlung von selbstsignierten Zertifikaten
*/
export class ExternalAPI {
private baseURL: string;
export interface Job {
id: string;
printer_id: string;
user_id: string;
start_time: string;
end_time: string;
status: string;
}
// Fetcher für SWR mit Fehlerbehandlung
const fetchWithErrorHandling = async (url: string) => {
const response = await fetch(url);
if (!response.ok) {
const error = new Error('Ein Fehler ist bei der API-Anfrage aufgetreten');
throw error;
constructor() {
this.baseURL = API_BASE_URL;
}
return response.json();
};
// API-Funktionen
export const api = {
// Drucker-Endpunkte
printers: {
getAll: () => fetchWithErrorHandling(API_ENDPOINTS.PRINTERS),
getById: (id: string) => fetchWithErrorHandling(`${API_ENDPOINTS.PRINTERS}/${id}`),
create: (data: Partial<Printer>) =>
fetch(API_ENDPOINTS.PRINTERS, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}).then(res => res.json()),
update: (id: string, data: Partial<Printer>) =>
fetch(`${API_ENDPOINTS.PRINTERS}/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}).then(res => res.json()),
delete: (id: string) =>
fetch(`${API_ENDPOINTS.PRINTERS}/${id}`, {
method: 'DELETE',
}).then(res => res.json()),
},
// Jobs-Endpunkte
jobs: {
getAll: () => fetchWithErrorHandling(API_ENDPOINTS.JOBS),
getById: (id: string) => fetchWithErrorHandling(`${API_ENDPOINTS.JOBS}/${id}`),
create: (data: Partial<Job>) =>
fetch(API_ENDPOINTS.JOBS, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}).then(res => res.json()),
update: (id: string, data: Partial<Job>) =>
fetch(`${API_ENDPOINTS.JOBS}/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}).then(res => res.json()),
delete: (id: string) =>
fetch(`${API_ENDPOINTS.JOBS}/${id}`, {
method: 'DELETE',
}).then(res => res.json()),
},
};
/**
* Führt einen API-Request durch mit Unterstützung für selbstsignierte Zertifikate
* im Entwicklungsmodus
*/
async fetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const url = `${this.baseURL}${endpoint}`;
try {
const response = await fetch(url, {
...options,
headers: {
'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) {
throw new Error(`API Error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('API Fehler:', 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.');
}
throw error;
}
}
// API-Methoden für verschiedene Endpoints
async getPrinters() {
return this.fetch<any>('/api/printers');
}
async getJobs() {
return this.fetch<any>('/api/jobs');
}
}