From cf0f79d0ffd63f4307c59db34e410a6d13c48b30 Mon Sep 17 00:00:00 2001 From: litoral05 Date: Wed, 3 Jun 2026 10:16:48 +0100 Subject: [PATCH] Add client persistence --- data/backend-gateway.db | Bin 0 -> 28672 bytes pom.xml | 20 ++++++ .../BackendGatewayApplication.java | 6 +- .../backend_gateway/client/ClientEntity.java | 54 ++++++++++++++- .../client/ClientRepository.java | 10 ++- .../config/WebClientConfig.java | 15 +++- .../gateway/BackendProxyService.java | 65 +++++++++++++----- .../gateway/ProxyProperties.java | 37 ++++++++++ src/main/resources/application.yaml | 19 ++++- .../db/migration/V1__create_clients.sql | 7 ++ 10 files changed, 208 insertions(+), 25 deletions(-) create mode 100644 data/backend-gateway.db create mode 100644 src/main/java/com/litoralregas/backend_gateway/gateway/ProxyProperties.java create mode 100644 src/main/resources/db/migration/V1__create_clients.sql diff --git a/data/backend-gateway.db b/data/backend-gateway.db new file mode 100644 index 0000000000000000000000000000000000000000..093ae6a6f5a2f80c9df00e73771146500c4bef49 GIT binary patch literal 28672 zcmeI((NEey90%|g5C(>^#0MUHxsVWO6jv~l$zE2mn`BTF%OVd;Q_2w=p(rg=9-WDQ z%U+i3Z`fb8EPL1$iino?g?uk<=(WGQ_CCLh@rs_FR85DH?qJy09kR>4;&`5WPYB0x z0k%ijJ}W_X5SmrkC;#Yp*mOZW$$`hH!i5v(b~FAwb8J*80!U5%N_LmrVm{m z${(a=RV8^Z&=^wPp&gB_WESr^3oJJq4fb%FTB*sWs#H6U$AsKkI4N!NoN0CFji;rL zoWZ27c|+Io-a+{GWiXN~7x~-QlYnk-e65c)+c>9vT{|~zXE0<{&FtLxUJ7}z73EMq zBPrjQY4a4RD@y`WB%88F2Ftvi67J6VNU~Vu?+24S`u6C1GO&278_V-2n|!ff#Lh8iPx=$hMLkYHRO?|a%^%fE1c*u>e+Cw_8lLxBJUAOHafKmY;|fB*y_009U<;6D*i`1NQ) z*w_!n-$kSO=?gF7y_-5cseaCD+Vte?&D9Qjr4;Dx_Fop&J w2tWV=5P$##AOHafKmY;|U;_C5AJYH=5P$##AOHafKmY;|fB*y_u=)c30MnLy*#H0l literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index a48234b..4b6c18c 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,26 @@ 17 + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.xerial + sqlite-jdbc + runtime + + + + org.hibernate.orm + hibernate-community-dialects + + + + org.flywaydb + flyway-core + org.springframework.boot spring-boot-starter-security diff --git a/src/main/java/com/litoralregas/backend_gateway/BackendGatewayApplication.java b/src/main/java/com/litoralregas/backend_gateway/BackendGatewayApplication.java index e462a43..ac16304 100644 --- a/src/main/java/com/litoralregas/backend_gateway/BackendGatewayApplication.java +++ b/src/main/java/com/litoralregas/backend_gateway/BackendGatewayApplication.java @@ -1,13 +1,15 @@ package com.litoralregas.backend_gateway; +import com.litoralregas.backend_gateway.gateway.ProxyProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; @SpringBootApplication +@EnableConfigurationProperties(ProxyProperties.class) public class BackendGatewayApplication { public static void main(String[] args) { SpringApplication.run(BackendGatewayApplication.class, args); } - -} +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/backend_gateway/client/ClientEntity.java b/src/main/java/com/litoralregas/backend_gateway/client/ClientEntity.java index 6bee074..65acd1f 100644 --- a/src/main/java/com/litoralregas/backend_gateway/client/ClientEntity.java +++ b/src/main/java/com/litoralregas/backend_gateway/client/ClientEntity.java @@ -1,4 +1,56 @@ package com.litoralregas.backend_gateway.client; +import jakarta.persistence.*; + +@Entity +@Table(name = "clients") public class ClientEntity { -} + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String name; + + @Column(name = "backend_base_url", nullable = false) + private String backendBaseUrl; + + @Column(nullable = false) + private boolean enabled = true; + + @Column(name = "created_at", nullable = false, insertable = false, updatable = false) + private String createdAt; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getBackendBaseUrl() { + return backendBaseUrl; + } + + public boolean isEnabled() { + return enabled; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setName(String name) { + this.name = name; + } + + public void setBackendBaseUrl(String backendBaseUrl) { + this.backendBaseUrl = backendBaseUrl; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/backend_gateway/client/ClientRepository.java b/src/main/java/com/litoralregas/backend_gateway/client/ClientRepository.java index 49901f1..bb43487 100644 --- a/src/main/java/com/litoralregas/backend_gateway/client/ClientRepository.java +++ b/src/main/java/com/litoralregas/backend_gateway/client/ClientRepository.java @@ -1,4 +1,10 @@ package com.litoralregas.backend_gateway.client; -public class ClientRepository { -} +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface ClientRepository extends JpaRepository { + + Optional findByName(String name); +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/backend_gateway/config/WebClientConfig.java b/src/main/java/com/litoralregas/backend_gateway/config/WebClientConfig.java index 5b9e035..bbac12d 100644 --- a/src/main/java/com/litoralregas/backend_gateway/config/WebClientConfig.java +++ b/src/main/java/com/litoralregas/backend_gateway/config/WebClientConfig.java @@ -1,14 +1,25 @@ package com.litoralregas.backend_gateway.config; +import com.litoralregas.backend_gateway.gateway.ProxyProperties; +import io.netty.channel.ChannelOption; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; +import reactor.netty.http.client.HttpClient; @Configuration public class WebClientConfig { @Bean - public WebClient webClient(WebClient.Builder builder) { - return builder.build(); + public WebClient webClient(WebClient.Builder builder, ProxyProperties proxyProperties) { + HttpClient httpClient = HttpClient.create() + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, + Math.toIntExact(proxyProperties.getConnectTimeout().toMillis())) + .responseTimeout(proxyProperties.getResponseTimeout()); + + return builder + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .build(); } } \ No newline at end of file diff --git a/src/main/java/com/litoralregas/backend_gateway/gateway/BackendProxyService.java b/src/main/java/com/litoralregas/backend_gateway/gateway/BackendProxyService.java index 1b9f6e7..97c0c27 100644 --- a/src/main/java/com/litoralregas/backend_gateway/gateway/BackendProxyService.java +++ b/src/main/java/com/litoralregas/backend_gateway/gateway/BackendProxyService.java @@ -1,48 +1,79 @@ package com.litoralregas.backend_gateway.gateway; import jakarta.servlet.http.HttpServletRequest; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; @Service public class BackendProxyService { private final WebClient webClient; - private final String devBackendUrl; + private final ProxyProperties proxyProperties; - public BackendProxyService( - WebClient webClient, - @Value("${gateway.dev-backend-url}") String devBackendUrl - ) { + public BackendProxyService(WebClient webClient, ProxyProperties proxyProperties) { this.webClient = webClient; - this.devBackendUrl = devBackendUrl; + this.proxyProperties = proxyProperties; } public String getHealth() { return webClient.get() - .uri(devBackendUrl + "/actuator/health") + .uri(proxyProperties.getBackendBaseUrl() + "/actuator/health") .retrieve() .bodyToMono(String.class) .block(); } public ResponseEntity proxy(HttpServletRequest request, String body) { + String path = request.getRequestURI().replaceFirst("/api/backend", ""); String query = request.getQueryString(); - String targetUrl = devBackendUrl + path + (query != null ? "?" + query : ""); + String targetUrl = + proxyProperties.getBackendBaseUrl() + + path + + (query != null ? "?" + query : ""); - String response = webClient - .method(HttpMethod.valueOf(request.getMethod())) - .uri(targetUrl) - .bodyValue(body != null ? body : "") - .retrieve() - .bodyToMono(String.class) - .block(); + try { + WebClient.RequestBodySpec requestSpec = webClient + .method(HttpMethod.valueOf(request.getMethod())) + .uri(targetUrl); - return ResponseEntity.ok(response); + String contentType = request.getContentType(); + + if (contentType != null) { + requestSpec.header("Content-Type", contentType); + } + + String accept = request.getHeader("Accept"); + + if (accept != null) { + requestSpec.header("Accept", accept); + } + + ResponseEntity response = requestSpec + .bodyValue(body != null ? body : "") + .retrieve() + .toEntity(String.class) + .block(); + + return ResponseEntity + .status(response.getStatusCode()) + .headers(response.getHeaders()) + .body(response.getBody()); + + } catch (WebClientResponseException ex) { + return ResponseEntity + .status(ex.getStatusCode()) + .body(ex.getResponseBodyAsString()); + + } catch (Exception ex) { + return ResponseEntity + .status(HttpStatus.BAD_GATEWAY) + .body("Backend unavailable"); + } } } \ No newline at end of file diff --git a/src/main/java/com/litoralregas/backend_gateway/gateway/ProxyProperties.java b/src/main/java/com/litoralregas/backend_gateway/gateway/ProxyProperties.java new file mode 100644 index 0000000..2417342 --- /dev/null +++ b/src/main/java/com/litoralregas/backend_gateway/gateway/ProxyProperties.java @@ -0,0 +1,37 @@ +package com.litoralregas.backend_gateway.gateway; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.time.Duration; + +@ConfigurationProperties(prefix = "gateway.proxy") +public class ProxyProperties { + + private String backendBaseUrl; + private Duration connectTimeout = Duration.ofSeconds(3); + private Duration responseTimeout = Duration.ofSeconds(10); + + public String getBackendBaseUrl() { + return backendBaseUrl; + } + + public void setBackendBaseUrl(String backendBaseUrl) { + this.backendBaseUrl = backendBaseUrl; + } + + public Duration getConnectTimeout() { + return connectTimeout; + } + + public void setConnectTimeout(Duration connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public Duration getResponseTimeout() { + return responseTimeout; + } + + public void setResponseTimeout(Duration responseTimeout) { + this.responseTimeout = responseTimeout; + } +} \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 591f5e0..182d354 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -5,5 +5,22 @@ spring: application: name: backend-gateway + datasource: + url: jdbc:sqlite:./data/backend-gateway.db + driver-class-name: org.sqlite.JDBC + + jpa: + database-platform: org.hibernate.community.dialect.SQLiteDialect + hibernate: + ddl-auto: none + show-sql: true + + flyway: + enabled: true + locations: classpath:db/migration + gateway: - dev-backend-url: http://10.100.1.2:18450 \ No newline at end of file + proxy: + backend-base-url: http://10.100.1.2:18450 + connect-timeout: 3s + response-timeout: 10s \ No newline at end of file diff --git a/src/main/resources/db/migration/V1__create_clients.sql b/src/main/resources/db/migration/V1__create_clients.sql new file mode 100644 index 0000000..d784945 --- /dev/null +++ b/src/main/resources/db/migration/V1__create_clients.sql @@ -0,0 +1,7 @@ +CREATE TABLE clients ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + backend_base_url TEXT NOT NULL, + enabled INTEGER NOT NULL DEFAULT 1, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file