'GET', 'callback' => [$this, 'proxy_health_check'], 'permission_callback' => ['Skrift_Konfigurator_Admin_Settings', 'rest_api_key_permission'], ]); // Preview Batch generieren register_rest_route('skrift/v1', '/proxy/preview/batch', [ 'methods' => 'POST', 'callback' => [$this, 'proxy_preview_batch'], 'permission_callback' => ['Skrift_Konfigurator_Admin_Settings', 'rest_api_key_permission'], ]); // Einzelne Preview abrufen register_rest_route('skrift/v1', '/proxy/preview/(?P[a-zA-Z0-9_-]+)/(?P\d+)', [ 'methods' => 'GET', 'callback' => [$this, 'proxy_preview_get'], 'permission_callback' => ['Skrift_Konfigurator_Admin_Settings', 'rest_api_key_permission'], ]); // Order generieren register_rest_route('skrift/v1', '/proxy/order/generate', [ 'methods' => 'POST', 'callback' => [$this, 'proxy_order_generate'], 'permission_callback' => ['Skrift_Konfigurator_Admin_Settings', 'rest_api_key_permission'], ]); // Order finalisieren register_rest_route('skrift/v1', '/proxy/order/finalize', [ 'methods' => 'POST', 'callback' => [$this, 'proxy_order_finalize'], 'permission_callback' => ['Skrift_Konfigurator_Admin_Settings', 'rest_api_key_permission'], ]); // Motiv hochladen (ans Docker-Backend weiterleiten) register_rest_route('skrift/v1', '/proxy/motif/upload', [ 'methods' => 'POST', 'callback' => [$this, 'proxy_motif_upload'], 'permission_callback' => ['Skrift_Konfigurator_Admin_Settings', 'rest_api_key_permission'], ]); } /** * Holt Backend-Konfiguration */ private function get_backend_config(): array { $settings = Skrift_Konfigurator_Admin_Settings::get_settings(); return [ 'api_url' => $settings['backend_connection']['api_url'] ?? '', 'api_token' => $settings['backend_connection']['api_token'] ?? '', ]; } /** * Führt einen HTTP-Request ans Backend aus */ private function make_backend_request(string $method, string $endpoint, array $body = null): array { $config = $this->get_backend_config(); if (empty($config['api_url'])) { return [ 'success' => false, 'error' => 'Backend API URL nicht konfiguriert', 'status' => 500, ]; } $url = rtrim($config['api_url'], '/') . $endpoint; $args = [ 'method' => $method, 'timeout' => 60, 'headers' => [ 'Content-Type' => 'application/json', ], ]; // API Token hinzufügen wenn vorhanden if (!empty($config['api_token'])) { $args['headers']['X-API-Token'] = $config['api_token']; } // Body hinzufügen bei POST/PUT if ($body !== null && in_array($method, ['POST', 'PUT', 'PATCH'])) { $args['body'] = wp_json_encode($body); } $response = wp_remote_request($url, $args); if (is_wp_error($response)) { return [ 'success' => false, 'error' => $response->get_error_message(), 'status' => 500, ]; } $status_code = wp_remote_retrieve_response_code($response); $response_body = wp_remote_retrieve_body($response); // Versuche JSON zu parsen $data = json_decode($response_body, true); if ($status_code >= 200 && $status_code < 300) { return [ 'success' => true, 'data' => $data ?? $response_body, 'status' => $status_code, ]; } return [ 'success' => false, 'error' => $data['error'] ?? $data['message'] ?? 'Backend-Fehler', 'status' => $status_code, 'data' => $data, ]; } /** * Health Check Proxy */ public function proxy_health_check($request) { $result = $this->make_backend_request('GET', '/health'); if (!$result['success']) { return new WP_Error('backend_error', $result['error'], ['status' => $result['status']]); } return $result['data']; } /** * Preview Batch Proxy */ public function proxy_preview_batch($request) { $body = $request->get_json_params(); if (empty($body)) { return new WP_Error('invalid_request', 'Keine Daten empfangen', ['status' => 400]); } $result = $this->make_backend_request('POST', '/api/preview/batch', $body); if (!$result['success']) { // Rate Limiting Info weitergeben if ($result['status'] === 429 && isset($result['data']['retryAfter'])) { return new WP_REST_Response([ 'error' => $result['error'], 'retryAfter' => $result['data']['retryAfter'], ], 429); } return new WP_Error('backend_error', $result['error'], ['status' => $result['status']]); } return $result['data']; } /** * Einzelne Preview abrufen Proxy */ public function proxy_preview_get($request) { $session_id = $request->get_param('sessionId'); $index = $request->get_param('index'); // Sicherheits-Validierung: Session-ID darf nur alphanumerische Zeichen, Unterstriche und Bindestriche enthalten if (!preg_match('/^[a-zA-Z0-9_-]+$/', $session_id)) { return new WP_Error('invalid_session_id', 'Ungültige Session-ID', ['status' => 400]); } // Index muss eine positive Ganzzahl sein $index = absint($index); $result = $this->make_backend_request('GET', "/api/preview/{$session_id}/{$index}"); if (!$result['success']) { return new WP_Error('backend_error', $result['error'], ['status' => $result['status']]); } // Bei SVG-Daten: Content-Type setzen if (is_string($result['data']) && strpos($result['data'], 'header('Content-Type', 'image/svg+xml'); return $response; } return $result['data']; } /** * Order generieren Proxy */ public function proxy_order_generate($request) { $body = $request->get_json_params(); if (empty($body)) { return new WP_Error('invalid_request', 'Keine Daten empfangen', ['status' => 400]); } $result = $this->make_backend_request('POST', '/api/order/generate', $body); if (!$result['success']) { return new WP_Error('backend_error', $result['error'], ['status' => $result['status']]); } return $result['data']; } /** * Order finalisieren Proxy */ public function proxy_order_finalize($request) { $body = $request->get_json_params(); if (empty($body)) { return new WP_Error('invalid_request', 'Keine Daten empfangen', ['status' => 400]); } $result = $this->make_backend_request('POST', '/api/order/finalize', $body); if (!$result['success']) { return new WP_Error('backend_error', $result['error'], ['status' => $result['status']]); } return $result['data']; } /** * Motiv hochladen Proxy * Leitet Datei ans Docker-Backend weiter, speichert dort im Auftragsordner */ public function proxy_motif_upload($request) { // Datei aus Request holen $files = $request->get_file_params(); if (empty($files['motif'])) { return new WP_Error('no_file', 'Keine Datei empfangen', ['status' => 400]); } $file = $files['motif']; $order_number = $request->get_param('orderNumber'); if (empty($order_number)) { return new WP_Error('no_order_number', 'Bestellnummer fehlt', ['status' => 400]); } // Überprüfe auf Upload-Fehler if ($file['error'] !== UPLOAD_ERR_OK) { return new WP_Error('upload_error', 'Upload-Fehler: ' . $file['error'], ['status' => 400]); } // Erlaubte Dateitypen prüfen $allowed_types = ['image/png', 'image/jpeg', 'image/jpg', 'image/webp', 'image/svg+xml', 'application/pdf']; $file_type = wp_check_filetype($file['name']); // MIME-Type Prüfung $finfo = finfo_open(FILEINFO_MIME_TYPE); $real_type = finfo_file($finfo, $file['tmp_name']); finfo_close($finfo); // SVG wird oft als text/xml erkannt if ($real_type === 'text/xml' || $real_type === 'text/plain') { $content = file_get_contents($file['tmp_name']); if (strpos($content, ' 400]); } // Datei ans Docker-Backend senden (multipart/form-data) $config = $this->get_backend_config(); if (empty($config['api_url'])) { return new WP_Error('no_backend', 'Backend API URL nicht konfiguriert', ['status' => 500]); } $url = rtrim($config['api_url'], '/') . '/api/order/motif'; // Boundary für multipart $boundary = wp_generate_password(24, false); // Multipart Body bauen $body = ''; // orderNumber Feld $body .= "--{$boundary}\r\n"; $body .= "Content-Disposition: form-data; name=\"orderNumber\"\r\n\r\n"; $body .= $order_number . "\r\n"; // Datei Feld $body .= "--{$boundary}\r\n"; $body .= "Content-Disposition: form-data; name=\"motif\"; filename=\"" . basename($file['name']) . "\"\r\n"; $body .= "Content-Type: " . ($real_type ?: 'application/octet-stream') . "\r\n\r\n"; $body .= file_get_contents($file['tmp_name']) . "\r\n"; $body .= "--{$boundary}--\r\n"; $args = [ 'method' => 'POST', 'timeout' => 60, 'headers' => [ 'Content-Type' => 'multipart/form-data; boundary=' . $boundary, ], 'body' => $body, ]; // API Token hinzufügen wenn vorhanden if (!empty($config['api_token'])) { $args['headers']['X-API-Token'] = $config['api_token']; } $response = wp_remote_post($url, $args); if (is_wp_error($response)) { return new WP_Error('backend_error', $response->get_error_message(), ['status' => 500]); } $status_code = wp_remote_retrieve_response_code($response); $response_body = wp_remote_retrieve_body($response); $data = json_decode($response_body, true); if ($status_code >= 200 && $status_code < 300) { return $data; } return new WP_Error('backend_error', $data['error'] ?? $data['message'] ?? 'Upload fehlgeschlagen', ['status' => $status_code]); } } new Skrift_Konfigurator_API_Proxy();