diff --git a/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java b/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java index 4289475..98c6f52 100644 --- a/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java +++ b/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java @@ -2,134 +2,142 @@ package fr.gameovergne.api.controller.prestashop; import fr.gameovergne.api.service.prestashop.PrestashopClient; import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.util.AntPathMatcher; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerMapping; + +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; @RestController -@RequestMapping("/api/ps") // <-- IMPORTANT : préfixe unique pour tout le proxy Presta -@RequiredArgsConstructor -@Slf4j +@RequestMapping("/api/ps") public class PrestashopProxyController { + Logger log = LoggerFactory.getLogger(PrestashopProxyController.class); + private final PrestashopClient prestashopClient; - /** - * Extrait le path Presta à partir de l'URL appelée côté front. - * - * Exemple : - * contextPath = /gameovergne-api - * requestURI = /gameovergne-api/api/ps/categories - * prefixToStrip = /gameovergne-api/api/ps - * => pathForwarded = /categories - */ - private String extractPrestaPath(HttpServletRequest request) { - String requestUri = request.getRequestURI(); - String prefix = request.getContextPath() + "/api/ps"; - if (!requestUri.startsWith(prefix)) { - // Sécurité, mais normalement ça ne doit jamais arriver - log.warn("extractPrestaPath: URI {} ne commence pas par prefix {}", requestUri, prefix); - return requestUri; - } - String path = requestUri.substring(prefix.length()); // garde le '/' de début - if (path.isEmpty()) { - path = "/"; // Cas extrême, normalement on ne l’utilise pas - } - return path; + public PrestashopProxyController(PrestashopClient prestashopClient) { + this.prestashopClient = prestashopClient; } - // ---------- GET générique /api/ps/** ---------- + // --- Helpers communs --- - @GetMapping(path = "/**", produces = MediaType.APPLICATION_JSON_VALUE) + private String extractPath(HttpServletRequest request) { + String fullPath = (String) request.getAttribute( + HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE + ); + String bestMatchPattern = (String) request.getAttribute( + HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE + ); + + String relativePath = new AntPathMatcher() + .extractPathWithinPattern(bestMatchPattern, fullPath); + + // On renvoie toujours avec un "/" devant + return "/" + relativePath; + } + + private String extractDecodedQuery(HttpServletRequest request) { + String rawQuery = request.getQueryString(); + if (rawQuery != null) { + rawQuery = URLDecoder.decode(rawQuery, StandardCharsets.UTF_8); + } + return rawQuery; + } + + // ---------- GET ---------- + @GetMapping("/**") public ResponseEntity proxyGet(HttpServletRequest request) { - String path = extractPrestaPath(request); // ex: /categories - String rawQuery = request.getQueryString(); // ex: display=[id,name,active]&output_format=JSON + String path = extractPath(request); + String rawQuery = extractDecodedQuery(request); - log.info("[Proxy] GET {}", path); + ResponseEntity prestaResponse = + prestashopClient.getWithRawQuery(path, rawQuery); - try { - return prestashopClient.getWithRawQuery(path, rawQuery); - } catch (Exception ex) { - log.error("[Proxy] GET error on {}?{} ", path, rawQuery, ex); - // On renvoie une 502 propre plutôt que laisser Spring balancer un stacktrace - return ResponseEntity - .status(502) - .contentType(MediaType.APPLICATION_JSON) - .body(""" - {"error":"Error while calling PrestaShop WebService"} - """); - } + return ResponseEntity + .status(prestaResponse.getStatusCode()) + .contentType(MediaType.APPLICATION_JSON) + .body(prestaResponse.getBody()); } - // ---------- POST générique XML /api/ps/** (catégories, produits, etc.) ---------- - - @PostMapping( - path = "/**", - consumes = MediaType.APPLICATION_XML_VALUE, - produces = MediaType.APPLICATION_XML_VALUE - ) + // ---------- POST ---------- + @PostMapping("/**") public ResponseEntity proxyPost(HttpServletRequest request, @RequestBody String xmlBody) { + String path = extractPath(request); + String rawQuery = extractDecodedQuery(request); - String path = extractPrestaPath(request); // ex: /categories - String rawQuery = request.getQueryString(); + log.info("XML envoyé à Presta:\n{}", xmlBody); - log.info("XML envoyé à Presta (POST {}):\n{}", path, xmlBody); + ResponseEntity prestaResponse = + prestashopClient.postWithRawQuery(path, rawQuery, xmlBody); - return prestashopClient.postWithRawQuery(path, rawQuery, xmlBody); + return ResponseEntity + .status(prestaResponse.getStatusCode()) + .contentType(MediaType.APPLICATION_XML) + .body(prestaResponse.getBody()); } - // ---------- PUT générique XML /api/ps/** ---------- - - @PutMapping( - path = "/**", - consumes = MediaType.APPLICATION_XML_VALUE, - produces = MediaType.APPLICATION_XML_VALUE - ) + // ---------- PUT ---------- + @PutMapping("/**") public ResponseEntity proxyPut(HttpServletRequest request, @RequestBody String xmlBody) { + String path = extractPath(request); + String rawQuery = extractDecodedQuery(request); - String path = extractPrestaPath(request); // ex: /products/446 - String rawQuery = request.getQueryString(); + log.info("XML envoyé à Presta:\n{}", xmlBody); - log.info("XML envoyé (proxy PUT {}):\n{}", path, xmlBody); + ResponseEntity prestaResponse = + prestashopClient.putWithRawQuery(path, rawQuery, xmlBody); - return prestashopClient.putWithRawQuery(path, rawQuery, xmlBody); + return ResponseEntity + .status(prestaResponse.getStatusCode()) + .contentType(MediaType.APPLICATION_XML) + .body(prestaResponse.getBody()); } - // ---------- DELETE générique /api/ps/** ---------- - - @DeleteMapping(path = "/**", produces = MediaType.APPLICATION_XML_VALUE) + // ---------- DELETE ---------- + @DeleteMapping("/**") public ResponseEntity proxyDelete(HttpServletRequest request) { + String path = extractPath(request); + String rawQuery = extractDecodedQuery(request); - String path = extractPrestaPath(request); // ex: /categories/10 - String rawQuery = request.getQueryString(); + ResponseEntity prestaResponse = + prestashopClient.deleteWithRawQuery(path, rawQuery); - log.info("[Proxy] DELETE {}", path); - - return prestashopClient.deleteWithRawQuery(path, rawQuery); + return ResponseEntity + .status(prestaResponse.getStatusCode()) + .contentType(MediaType.APPLICATION_XML) + .body(prestaResponse.getBody()); } - // ---------- UPLOAD IMAGE PRODUIT /api/ps/images/products/{productId} ---------- + /** + * Upload d'une image produit : + * Front → (multipart/form-data) → /api/ps/images/products/{productId} + * Backend → (bytes) → https://shop.gameovergne.fr/api/images/products/{productId} + */ @PostMapping( - path = "/images/products/{productId}", + path = "/api/ps/images/products/{productId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_XML_VALUE ) - public ResponseEntity uploadProductImage(HttpServletRequest request, - @PathVariable("productId") String productId, - @RequestPart("image") MultipartFile imageFile) { - - String rawQuery = request.getQueryString(); + public ResponseEntity uploadProductImage( + jakarta.servlet.http.HttpServletRequest request, + @PathVariable("productId") String productId, + @RequestPart("image") MultipartFile imageFile + ) { + String rawQuery = request.getQueryString(); // si jamais tu ajoutes des options côté Presta log.info("[Proxy] Upload image produit {} (size={} bytes, ct={})", productId, imageFile.getSize(), imageFile.getContentType()); - // Là on ne passe PAS par extractPrestaPath : on a un endpoint dédié côté PrestashopClient return prestashopClient.uploadProductImage(productId, rawQuery, imageFile); } } \ No newline at end of file