Skip to content

Commit ff0d522

Browse files
authored
#61 Add backend Spring Cloud Gateway (#66)
Signed-off-by: vityaman <vityaman.dev@yandex.ru>
1 parent 3996cca commit ff0d522

File tree

16 files changed

+272
-23
lines changed

16 files changed

+272
-23
lines changed

backend/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
For building and testing a project check the [GitHub Workflow](../.github/workflows/gradle.yml).
66

77
To run the backend you need to make Spring Boot jars and run docker compose, but before do not forget to prepare environment variables and secret keys.
8+
But before ensure that frontend is already built.
89

910
```bash
1011
source config/env/local.sh

backend/authik/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM eclipse-temurin:21-jdk-alpine
1+
FROM eclipse-temurin:23-jdk-alpine
22

33
WORKDIR /matchmaker
44

backend/buildSrc/build.gradle.kts

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ dependencies {
2626
implementation("org.jooq:jooq-codegen:$jooqVersion")
2727
implementation("org.jooq:jooq-codegen-gradle:$jooqVersion")
2828

29-
implementation("org.springframework.boot:spring-boot-gradle-plugin:3.3.5")
30-
implementation("io.spring.gradle:dependency-management-plugin:1.1.6")
29+
implementation("org.springframework.boot:spring-boot-gradle-plugin:3.4.1")
30+
implementation("io.spring.gradle:dependency-management-plugin:1.1.7")
3131
}

backend/config/crypto/keys.bash

+17-13
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,25 @@ MODE="$1"
66
ENV="$2"
77

88
ALIAS="itmo-dating"
9+
ALIAS_BACKEND="$ALIAS-backend"
910
KEYSTORE="keystore.p12"
10-
INSTALL_PATH="foundation/src/main/resources/keystore"
11+
BACKEND_INSTALL_PATH="foundation/src/main/resources/keystore"
12+
GATEWAY_INSTALL_PATH="gateway/src/main/resources/keystore"
1113
PASSWORD="$ITMO_DATING_KEY_STORE_PASSWORD"
1214

1315
function copy() {
14-
mkdir -p "../../$INSTALL_PATH"
15-
cp "$1" "../../$INSTALL_PATH/$1"
16+
mkdir -p "../../$BACKEND_INSTALL_PATH"
17+
mkdir -p "../../$GATEWAY_INSTALL_PATH"
18+
cp "$1" "../../$BACKEND_INSTALL_PATH/$1"
19+
cp "$1" "../../$GATEWAY_INSTALL_PATH/$1"
1620
}
1721

1822
function remove() {
19-
rm -f "$1" "../../$INSTALL_PATH/$1"
23+
rm -f "$1" "../../$BACKEND_INSTALL_PATH/$1" "../../$GATEWAY_INSTALL_PATH/$1"
2024
}
2125

2226
function generate() {
23-
echo "Generation key pair keystore..."
27+
echo "Generating the backend key pair keystore..."
2428
keytool \
2529
-genkeypair \
2630
-alias "$ALIAS" \
@@ -34,23 +38,23 @@ function generate() {
3438
-storeType PKCS12 \
3539
-storepass "$PASSWORD"
3640

37-
echo "Exporting a private key..."
38-
openssl pkcs12 -in "$KEYSTORE" -nocerts -out "$ALIAS-private.pem" \
41+
echo "Exporting the backend private key..."
42+
openssl pkcs12 -in "$KEYSTORE" -nocerts -out "$ALIAS_BACKEND-private.pem" \
3943
-passin pass:"$PASSWORD" -passout pass:"$PASSWORD"
4044

41-
echo "Exporting a public key..."
42-
openssl pkcs12 -in "$KEYSTORE" -nokeys -out "$ALIAS-public.pem" \
45+
echo "Exporting the backend public key..."
46+
openssl pkcs12 -in "$KEYSTORE" -nokeys -out "$ALIAS_BACKEND-public.pem" \
4347
-passin pass:"$PASSWORD" -passout pass:"$PASSWORD"
4448

4549
copy "$KEYSTORE"
46-
copy "$ALIAS-private.pem"
47-
copy "$ALIAS-public.pem"
50+
copy "$ALIAS_BACKEND-private.pem"
51+
copy "$ALIAS_BACKEND-public.pem"
4852
}
4953

5054
function clear() {
5155
remove "$KEYSTORE"
52-
remove "$ALIAS-private.pem"
53-
remove "$ALIAS-public.pem"
56+
remove "$ALIAS_BACKEND-private.pem"
57+
remove "$ALIAS_BACKEND-public.pem"
5458
}
5559

5660
if [ "$ENV" = "test" ]; then

backend/gateway/Dockerfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM eclipse-temurin:23-jdk-alpine
2+
3+
WORKDIR /gateway
4+
5+
COPY ./build/libs/gateway-1.0.0.jar ./gateway.jar
6+
7+
EXPOSE 8080
8+
9+
CMD ["java", "-jar", "gateway.jar"]

backend/gateway/build.gradle.kts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
plugins {
2+
id("buildlogic.spring-conventions")
3+
}
4+
5+
dependencies {
6+
implementation(libs.org.springframework.boot.spring.boot)
7+
implementation(libs.org.springframework.cloud.spring.cloud.starter.gateway)
8+
9+
implementation(libs.org.springdoc.springdoc.openapi.starter.webflux.ui)
10+
11+
testImplementation(libs.org.springframework.boot.spring.boot.starter.test)
12+
testImplementation(libs.junit.junit)
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package ru.ifmo.se.dating.gateway
2+
3+
import org.springframework.boot.autoconfigure.SpringBootApplication
4+
import org.springframework.boot.runApplication
5+
6+
@SpringBootApplication
7+
class Application
8+
9+
fun main(args: Array<String>) {
10+
runApplication<Application>(args = args)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package ru.ifmo.se.dating.gateway
2+
3+
import io.netty.handler.ssl.SslContext
4+
import io.netty.handler.ssl.SslContextBuilder
5+
import org.springframework.beans.factory.annotation.Value
6+
import org.springframework.context.annotation.Bean
7+
import org.springframework.context.annotation.Configuration
8+
import org.springframework.core.io.Resource
9+
import reactor.netty.http.client.HttpClient
10+
import java.security.KeyStore
11+
import javax.net.ssl.KeyManagerFactory
12+
import javax.net.ssl.TrustManagerFactory
13+
14+
@Configuration
15+
class SSLContextConfig(
16+
@Value("\${server.ssl.key-store-type}")
17+
private val keyStoreType: String,
18+
19+
@Value("\${server.ssl.key-store}")
20+
private val keyStore: Resource,
21+
22+
@Value("\${server.ssl.key-store-password}")
23+
private val keyStorePassword: String,
24+
25+
@Value("\${server.ssl.protocol}")
26+
private val sslProtocol: String,
27+
) {
28+
@Bean
29+
fun sslContext(): SslContext {
30+
val keystore = KeyStore.getInstance(keyStoreType)
31+
keyStore.inputStream.use { inputStream ->
32+
keystore.load(inputStream, keyStorePassword.toCharArray())
33+
}
34+
35+
val keyManager = KeyManagerFactory.getDefaultAlgorithm()
36+
.let { KeyManagerFactory.getInstance(it) }
37+
.apply { init(keystore, keyStorePassword.toCharArray()) }
38+
39+
val trustManager = TrustManagerFactory.getDefaultAlgorithm()
40+
.let { TrustManagerFactory.getInstance(it) }
41+
.apply { init(keystore) }
42+
43+
return SslContextBuilder.forClient()
44+
.keyManager(keyManager)
45+
.trustManager(trustManager)
46+
.protocols(sslProtocol)
47+
.build()
48+
}
49+
50+
@Bean
51+
fun httpClient(ssl: SslContext): HttpClient = HttpClient.create()
52+
.secure { sslSpec -> sslSpec.sslContext(ssl) }
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
server:
2+
ssl:
3+
enabled: true
4+
key-store-type: PKCS12
5+
key-store: classpath:keystore/keystore.p12
6+
key-store-password: ${KEY_STORE_PASSWORD}
7+
protocol: TLSv1.3
8+
enabled-protocols: TLSv1.3
9+
spring:
10+
cloud:
11+
gateway:
12+
routes:
13+
14+
- id: get-authik-openapi-api-yml
15+
uri: https://authik:8080
16+
predicates:
17+
- Method=GET
18+
- Path=/openapi/authik/api.yml
19+
filters:
20+
- RewritePath=/openapi/authik/api.yml, /openapi/api.yml
21+
22+
- id: get-matchmaker-openapi-api-yml
23+
uri: https://matchmaker:8080
24+
predicates:
25+
- Method=GET
26+
- Path=/openapi/matchmaker/api.yml
27+
filters:
28+
- RewritePath=/openapi/matchmaker/api.yml, /openapi/api.yml
29+
30+
- id: get-people-openapi-api-yml
31+
uri: https://people:8080
32+
predicates:
33+
- Method=GET
34+
- Path=/openapi/people/api.yml
35+
filters:
36+
- RewritePath=/openapi/people/api.yml, /openapi/api.yml
37+
38+
- id: put-authik-auth-telegram-web-app
39+
uri: https://authik:8080
40+
predicates:
41+
- Method=PUT
42+
- Path=/api/auth/telegram/web-app
43+
44+
- id: get-matchmaker-suggestions
45+
uri: https://matchmaker:8080
46+
predicates:
47+
- Method=GET
48+
- Path=/api/suggestions
49+
50+
- id: post-matchmaker-people-attitudes-incoming
51+
uri: https://matchmaker:8080
52+
predicates:
53+
- Method=POST
54+
- Path=/api/people/*/attitudes/incoming/*
55+
56+
- id: delete-matchmaker-attitudes
57+
uri: https://matchmaker:8080
58+
predicates:
59+
- Method=DELETE
60+
- Path=/api/attitudes
61+
62+
- id: get-matchmaker-people-matches
63+
uri: https://matchmaker:8080
64+
predicates:
65+
- Method=GET
66+
- Path=/api/people/{person_id}/matches
67+
68+
- id: get-people-person-id
69+
uri: https://people:8080
70+
predicates:
71+
- Method=GET
72+
- Path=/api/people/*
73+
74+
- id: delete-people-person-id
75+
uri: https://people:8080
76+
predicates:
77+
- Method=DELETE
78+
- Path=/api/people/*
79+
80+
- id: patch-people-person-id
81+
uri: https://people:8080
82+
predicates:
83+
- Method=PATCH
84+
- Path=/api/people/*
85+
86+
- id: get-people-faculties
87+
uri: https://people:8080
88+
predicates:
89+
- Method=GET
90+
- Path=/api/faculties
91+
92+
- id: get-people-locations
93+
uri: https://people:8080
94+
predicates:
95+
- Method=GET
96+
- Path=/api/locations
97+
98+
springdoc:
99+
swagger-ui:
100+
path: /swagger-ui.html
101+
urls:
102+
- name: authik
103+
url: openapi/authik/api.yml
104+
- name: matchmaker
105+
url: openapi/matchmaker/api.yml
106+
- name: people
107+
url: openapi/people/api.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package ru.ifmo.se.dating.gateway
2+
3+
import org.junit.Test
4+
import org.junit.runner.RunWith
5+
import org.springframework.boot.test.context.SpringBootTest
6+
import org.springframework.test.context.ActiveProfiles
7+
import org.springframework.test.context.ContextConfiguration
8+
import org.springframework.test.context.junit4.SpringRunner
9+
10+
@RunWith(SpringRunner::class)
11+
@ActiveProfiles(profiles = ["test"])
12+
@SpringBootTest(
13+
classes = [Application::class],
14+
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
15+
useMainMethod = SpringBootTest.UseMainMethod.ALWAYS,
16+
)
17+
@ContextConfiguration(initializers = [SecurityInitializer::class])
18+
class GatewayStartupTest {
19+
@Test
20+
fun contextLoads() {
21+
// Okay
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package ru.ifmo.se.dating.gateway
2+
3+
import org.springframework.boot.test.util.TestPropertyValues
4+
import org.springframework.context.ApplicationContextInitializer
5+
import org.springframework.context.ConfigurableApplicationContext
6+
7+
class SecurityInitializer :
8+
ApplicationContextInitializer<ConfigurableApplicationContext> {
9+
10+
private val keyStorePassword: String = "testing-keystore-password"
11+
12+
override fun initialize(ctx: ConfigurableApplicationContext) {
13+
TestPropertyValues.of(
14+
"server.ssl.key-store-password=$keyStorePassword",
15+
).applyTo(ctx.environment)
16+
}
17+
}

backend/gradle/libs.versions.toml

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[versions]
2-
org-springframework-boot-spring-boot = "3.3.5"
3-
org-springframework-spring = "6.1.14"
2+
org-springframework-boot-spring-boot = "3.4.1"
3+
org-springframework-spring = "6.2.1"
4+
org-springframework-cloud = "4.2.0"
45

56
io-swagger-core-v3-swagger = "2.2.25"
67
org-openapitools-jackson-databind-nullable = "0.2.6"
@@ -35,6 +36,8 @@ org-springframework-boot-spring-boot-starter-test = { module = "org.springframew
3536
org-springframework-spring-context = { module = "org.springframework:spring-context", version.ref = "org-springframework-spring" }
3637
org-springframework-spring-web = { module = "org.springframework:spring-web", version.ref = "org-springframework-spring" }
3738

39+
org-springframework-cloud-spring-cloud-starter-gateway = { module = "org.springframework.cloud:spring-cloud-starter-gateway", version.ref = "org-springframework-cloud" }
40+
3841
org-springdoc-springdoc-openapi-starter-webflux-ui = { module = "org.springdoc:springdoc-openapi-starter-webflux-ui", version.ref = "org-springdoc-springdoc-openapi-starter" }
3942

4043
io-swagger-core-v3-swagger-annotations = { module = "io.swagger.core.v3:swagger-annotations", version.ref = "io-swagger-core-v3-swagger" }

backend/matchmaker/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM eclipse-temurin:21-jdk-alpine
1+
FROM eclipse-temurin:23-jdk-alpine
22

33
WORKDIR /matchmaker
44

backend/people/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM eclipse-temurin:21-jdk-alpine
1+
FROM eclipse-temurin:23-jdk-alpine
22

33
WORKDIR /people
44

backend/settings.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ rootProject.name = "itmo-dating-backend"
77
include(
88
":foundation",
99
":foundation-test",
10+
":gateway",
1011
":authik",
1112
":matchmaker",
1213
":people",

0 commit comments

Comments
 (0)