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 05d0499..6973711 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 @@ -1,101 +1,141 @@ package fr.gameovergne.api.controller.prestashop; -import fr.gameovergne.api.service.prestashop.PrestashopClient; import jakarta.servlet.http.HttpServletRequest; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.stream.Collectors; @RestController @RequestMapping("/api/ps") public class PrestashopProxyController { - private final PrestashopClient prestashopClient; + private static final Logger log = LoggerFactory.getLogger(PrestashopProxyController.class); - public PrestashopProxyController(PrestashopClient prestashopClient) { - this.prestashopClient = prestashopClient; + private final RestTemplate restTemplate = new RestTemplate(); + + private final String baseUrl; // ex : https://shop.gameovergne.fr + private final String basicAuth; // base64 SANS le "Basic " + + public PrestashopProxyController( + @Value("${prestashop.base-url}") String baseUrl, + @Value("${prestashop.basic-auth}") String basicAuth + ) { + this.baseUrl = baseUrl; + this.basicAuth = basicAuth; } - private String extractPath(HttpServletRequest request) { - String fullPath = request.getRequestURI(); // ex: /api/ps/products/446 - String contextPath = request.getContextPath(); // souvent "" - String relative = fullPath.substring(contextPath.length()); // /api/ps/products/446 + // =============== Helpers =============== - // On enlève le préfixe /api/ps => /products/446 + private String extractPath(HttpServletRequest request) { + // /api/ps/products/446 -> /products/446 + String fullPath = request.getRequestURI(); + String contextPath = request.getContextPath(); // souvent "" + String relative = fullPath.substring(contextPath.length()); return relative.replaceFirst("^/api/ps", ""); } - /* =========================== - * GET - * =========================== */ + private String readBody(HttpServletRequest request) throws IOException { + try (BufferedReader reader = request.getReader()) { + return reader.lines().collect(Collectors.joining(System.lineSeparator())); + } + } + + private ResponseEntity forward(HttpServletRequest request, + HttpMethod method, + String bodyIfAny) { + String path = extractPath(request); + String rawQuery = request.getQueryString(); + + UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(baseUrl) + .path("/api") + .path(path); + + if (rawQuery != null && !rawQuery.isBlank()) { + builder.query(rawQuery); // on garde EXACTEMENT la query envoyée par Angular + } + + String url = builder.build(true).toUriString(); + log.info("[PrestaProxy] {} {}", method.name(), url); + + HttpHeaders headers = new HttpHeaders(); + + // Auth Presta (cachée côté backend) + headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); + + // On propage Accept/Content-Type du front pour garder le même comportement qu’en dev + String accept = request.getHeader(HttpHeaders.ACCEPT); + if (accept != null) { + headers.set(HttpHeaders.ACCEPT, accept); + } + + String contentType = request.getHeader(HttpHeaders.CONTENT_TYPE); + if (contentType != null) { + headers.set(HttpHeaders.CONTENT_TYPE, contentType); + } + + HttpEntity entity = new HttpEntity<>(bodyIfAny, headers); + + try { + ResponseEntity response = restTemplate.exchange( + url, + method, + entity, + String.class + ); + + // On renvoie EXACTEMENT le status + headers + body de Presta au front + return ResponseEntity + .status(response.getStatusCode()) + .headers(response.getHeaders()) + .body(response.getBody()); + + } catch (HttpStatusCodeException ex) { + // On propage aussi les erreurs Presta (4xx/5xx) sans les transformer en 500 Spring + HttpHeaders respHeaders = ex.getResponseHeaders() != null + ? ex.getResponseHeaders() + : new HttpHeaders(); + + log.error("[PrestaProxy] {} {} -> HTTP {}", + method.name(), url, ex.getStatusCode().value()); + + return ResponseEntity + .status(ex.getStatusCode()) + .headers(respHeaders) + .body(ex.getResponseBodyAsString()); + } + } + + // =============== Routes =============== @GetMapping("/**") public ResponseEntity proxyGet(HttpServletRequest request) { - String path = extractPath(request); - String rawQuery = request.getQueryString(); // on laisse Angular décider des filtres - - String body = prestashopClient.get(path, rawQuery); - - return ResponseEntity - .ok() - .contentType(MediaType.APPLICATION_JSON) - .body(body); + return forward(request, HttpMethod.GET, null); } - /* =========================== - * POST - * =========================== */ - - @PostMapping("/**") - public ResponseEntity proxyPost( - HttpServletRequest request, - @RequestBody String rawBody - ) { - String path = extractPath(request); - String rawQuery = request.getQueryString(); - - String body = prestashopClient.post(path, rawQuery, rawBody); - - return ResponseEntity - .ok() - .contentType(MediaType.APPLICATION_JSON) - .body(body); - } - - /* =========================== - * PUT - * =========================== */ - - @PutMapping("/**") - public ResponseEntity proxyPut( - HttpServletRequest request, - @RequestBody String rawBody - ) { - String path = extractPath(request); - String rawQuery = request.getQueryString(); - - String body = prestashopClient.put(path, rawQuery, rawBody); - - return ResponseEntity - .ok() - .contentType(MediaType.APPLICATION_JSON) - .body(body); - } - - /* =========================== - * DELETE - * =========================== */ - @DeleteMapping("/**") public ResponseEntity proxyDelete(HttpServletRequest request) { - String path = extractPath(request); - String rawQuery = request.getQueryString(); + return forward(request, HttpMethod.DELETE, null); + } - String body = prestashopClient.delete(path, rawQuery); + @PostMapping("/**") + public ResponseEntity proxyPost(HttpServletRequest request) throws IOException { + String body = readBody(request); // XML ou JSON, on ne touche à rien + return forward(request, HttpMethod.POST, body); + } - return ResponseEntity - .ok() - .contentType(MediaType.APPLICATION_JSON) - .body(body); + @PutMapping("/**") + public ResponseEntity proxyPut(HttpServletRequest request) throws IOException { + String body = readBody(request); + return forward(request, HttpMethod.PUT, body); } } \ No newline at end of file diff --git a/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java b/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java deleted file mode 100644 index be9369f..0000000 --- a/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java +++ /dev/null @@ -1,230 +0,0 @@ -package fr.gameovergne.api.service.prestashop; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.lang.Nullable; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -@Service -public class PrestashopClient { - - private static final Logger log = LoggerFactory.getLogger(PrestashopClient.class); - - private final RestTemplate restTemplate = new RestTemplate(); - - private final String baseUrl; - private final String basicAuth; // base64 SANS le "Basic " - - public PrestashopClient( - @Value("${prestashop.base-url}") String baseUrl, - @Value("${prestashop.basic-auth}") String basicAuth - ) { - this.baseUrl = baseUrl; - this.basicAuth = basicAuth; - } - - /* =========================== - * GET (proxy) - * =========================== */ - - /** - * GET générique vers PrestaShop. - * - * - On part de la query envoyée par le front (filters, price[use_tax], ...). - * - On force/se remplace uniquement output_format=JSON et display=full. - */ - public String get(String path, @Nullable String rawQuery) { - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(baseUrl) - .path("/api") - .path(path); - - // On remet la query EXACTEMENT comme envoyée par Angular - if (rawQuery != null && !rawQuery.isBlank()) { - builder.query(rawQuery); - } - - // On force juste ces deux paramètres - builder.replaceQueryParam("output_format", "JSON"); - builder.replaceQueryParam("display", "full"); - - String url = builder.build(true).toUriString(); - - log.info("[PrestaShop] GET {}", url); - - HttpHeaders headers = new HttpHeaders(); - headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); - - HttpEntity entity = new HttpEntity<>(headers); - - try { - ResponseEntity response = restTemplate.exchange( - url, - HttpMethod.GET, - entity, - String.class - ); - - log.info("[PrestaShop] Réponse HTTP {} pour {}", response.getStatusCode(), url); - - if (!response.getStatusCode().is2xxSuccessful()) { - throw new RuntimeException("PrestaShop returned non-2xx status: " - + response.getStatusCode() + " for URL " + url); - } - - return response.getBody(); - } catch (RestClientException e) { - log.error("[PrestaShop] Erreur GET {}", url, e); - throw new RuntimeException("Erreur GET PrestaShop", e); - } - } - - /* =========================== - * POST - * =========================== */ - - public String post(String path, @Nullable String rawQuery, String body) { - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(baseUrl) - .path("/api") - .path(path); - - if (rawQuery != null && !rawQuery.isBlank()) { - builder.query(rawQuery); - } - - // Pour POST, tu peux choisir de forcer output_format si tu veux la réponse en JSON - builder.replaceQueryParam("output_format", "JSON"); - - String url = builder.build(true).toUriString(); - - log.info("[PrestaShop] POST {}", url); - - HttpHeaders headers = new HttpHeaders(); - headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); - headers.setContentType(MediaType.APPLICATION_XML); // Presta attend du XML - - HttpEntity entity = new HttpEntity<>(body, headers); - - try { - ResponseEntity response = restTemplate.exchange( - url, - HttpMethod.POST, - entity, - String.class - ); - - log.info("[PrestaShop] Réponse HTTP {} pour {}", response.getStatusCode(), url); - - if (!response.getStatusCode().is2xxSuccessful()) { - throw new RuntimeException("PrestaShop returned non-2xx status: " - + response.getStatusCode() + " for URL " + url); - } - - return response.getBody(); - } catch (RestClientException e) { - log.error("[PrestaShop] Erreur POST {}", url, e); - throw new RuntimeException("Erreur POST PrestaShop", e); - } - } - - /* =========================== - * PUT - * =========================== */ - - public String put(String path, @Nullable String rawQuery, String body) { - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(baseUrl) - .path("/api") - .path(path); - - if (rawQuery != null && !rawQuery.isBlank()) { - builder.query(rawQuery); - } - - builder.replaceQueryParam("output_format", "JSON"); - - String url = builder.build(true).toUriString(); - - log.info("[PrestaShop] PUT {}", url); - - HttpHeaders headers = new HttpHeaders(); - headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); - headers.setContentType(MediaType.APPLICATION_XML); - - HttpEntity entity = new HttpEntity<>(body, headers); - - try { - ResponseEntity response = restTemplate.exchange( - url, - HttpMethod.PUT, - entity, - String.class - ); - - log.info("[PrestaShop] Réponse HTTP {} pour {}", response.getStatusCode(), url); - - if (!response.getStatusCode().is2xxSuccessful()) { - throw new RuntimeException("PrestaShop returned non-2xx status: " - + response.getStatusCode() + " for URL " + url); - } - - return response.getBody(); - } catch (RestClientException e) { - log.error("[PrestaShop] Erreur PUT {}", url, e); - throw new RuntimeException("Erreur PUT PrestaShop", e); - } - } - - /* =========================== - * DELETE - * =========================== */ - - public String delete(String path, @Nullable String rawQuery) { - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(baseUrl) - .path("/api") - .path(path); - - if (rawQuery != null && !rawQuery.isBlank()) { - builder.query(rawQuery); - } - - builder.replaceQueryParam("output_format", "JSON"); - - String url = builder.build(true).toUriString(); - - log.info("[PrestaShop] DELETE {}", url); - - HttpHeaders headers = new HttpHeaders(); - headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); - - HttpEntity entity = new HttpEntity<>(headers); - - try { - ResponseEntity response = restTemplate.exchange( - url, - HttpMethod.DELETE, - entity, - String.class - ); - - log.info("[PrestaShop] Réponse HTTP {} pour {}", response.getStatusCode(), url); - - if (!response.getStatusCode().is2xxSuccessful()) { - throw new RuntimeException("PrestaShop returned non-2xx status: " - + response.getStatusCode() + " for URL " + url); - } - - return response.getBody(); - } catch (RestClientException e) { - log.error("[PrestaShop] Erreur DELETE {}", url, e); - throw new RuntimeException("Erreur DELETE PrestaShop", e); - } - } -} \ No newline at end of file diff --git a/client/src/environments/environment.ts b/client/src/environments/environment.ts index 99295b6..365cc04 100644 --- a/client/src/environments/environment.ts +++ b/client/src/environments/environment.ts @@ -1,5 +1,5 @@ export const environment = { production: false, apiUrl: 'http://localhost:3000/api', - psUrl: '/ps' + psUrl: 'http://localhost:3000/api/ps' };