Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bcbda39ed4 | |||
| 554e8767d8 | |||
| 6938b764fc |
@@ -21,6 +21,8 @@
|
||||
}
|
||||
},
|
||||
"containerEnv": {
|
||||
"TESTCONTAINERS_RYUK_DISABLED": "true"
|
||||
}
|
||||
"TESTCONTAINERS_RYUK_DISABLED": "true",
|
||||
"QUARKUS_DEBUG_HOST": "0.0.0.0"
|
||||
},
|
||||
"forwardPorts": [5005]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
include:
|
||||
- ./docker-compose.kafka.yaml
|
||||
- ./docker-compose.postgres.yaml
|
||||
- ./docker-compose.redis.yaml
|
||||
|
||||
services:
|
||||
|
||||
@@ -5,6 +5,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -14,11 +18,15 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: shogo82148/actions-setup-redis@v1
|
||||
with:
|
||||
redis-version: "7.x"
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "25"
|
||||
java-version: "21"
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v4
|
||||
@@ -29,7 +37,7 @@ jobs:
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: Build and Test all modules
|
||||
run: ./mvnw -B clean verify -Dquarkus.container-image.build=false -Dquarkus.container-image.push=false
|
||||
run: ./mvnw -B install -Dquarkus.container-image.build=false -Dquarkus.container-image.push=false
|
||||
|
||||
- name: Publish Test Report
|
||||
uses: scacap/action-surefire-report@v1
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@
|
||||
[
|
||||
"@semantic-release/exec",
|
||||
{
|
||||
"prepareCmd": "./mvnw versions:set -DnewVersion=${nextRelease.version}",
|
||||
"publishCmd": "if [ \"${branch.name}\" = \"main\" ]; then ./mvnw clean package -Dquarkus.container-image.tag=${nextRelease.version} -DskipTests; fi"
|
||||
"prepareCmd": "./mvnw versions:set -DnewVersion=${nextRelease.version} && sed -i \"s|\\(image: git.demonkernel.io.vn/foodsurf/[^:]*\\):.*|\\1:${nextRelease.version}|g\" k8s.yaml",
|
||||
"publishCmd": "if [ \"${branch.name}\" = \"main\" ]; then ./mvnw package -Dquarkus.container-image.tag=${nextRelease.version} -DskipTests; fi"
|
||||
}
|
||||
],
|
||||
[
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.dtos.*;
|
||||
import com.drinkool.entities.CustomerEntity;
|
||||
import jakarta.inject.Inject;
|
||||
@@ -7,14 +8,14 @@ import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.*;
|
||||
|
||||
@Path("/api")
|
||||
@Path("")
|
||||
public class AuthenticationService {
|
||||
|
||||
@Inject
|
||||
OtpService otpService;
|
||||
|
||||
@POST
|
||||
@Path("/signup")
|
||||
@Path(Url.Signup)
|
||||
@Transactional
|
||||
public Response signup(Signup input) {
|
||||
CustomerEntity newCustomer = new CustomerEntity();
|
||||
@@ -25,7 +26,7 @@ public class AuthenticationService {
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/quickSignup")
|
||||
@Path(Url.QuickSignup)
|
||||
@Transactional
|
||||
public Response quickSignup(QuickSignup input) {
|
||||
CustomerEntity newCustomer = new CustomerEntity();
|
||||
@@ -36,7 +37,7 @@ public class AuthenticationService {
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/login")
|
||||
@Path(Url.Login)
|
||||
public Response login(Login input) {
|
||||
CustomerEntity customer = CustomerEntity.find(
|
||||
"phone",
|
||||
@@ -53,18 +54,7 @@ public class AuthenticationService {
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/sms_otp")
|
||||
public Response smsOtp(SmsOtp input) {
|
||||
String otp = otpService.generateAndSaveOtp(input.phone);
|
||||
|
||||
// Ở đây bạn sẽ gọi Kafka để gửi SMS thực tế
|
||||
System.out.println("OTP cho " + input.phone + " là: " + otp);
|
||||
|
||||
return Response.accepted().build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/quicklogin")
|
||||
@Path(Url.QuickLogin)
|
||||
public Response quickLogin(QuickLogin input) {
|
||||
if (otpService.verifyOtp(input.phone, input.otp)) {
|
||||
return Response.accepted().build();
|
||||
@@ -73,4 +63,15 @@ public class AuthenticationService {
|
||||
.entity("InvalidOTP")
|
||||
.build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path(Url.SmsOtp)
|
||||
public Response smsOtp(SmsOtp input) {
|
||||
String otp = otpService.generateAndSaveOtp(input.phone);
|
||||
|
||||
// Ở đây bạn sẽ gọi Kafka để gửi SMS thực tế
|
||||
System.out.println("OTP cho " + input.phone + " là: " + otp);
|
||||
|
||||
return Response.accepted().build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/postgres
|
||||
quarkus.hibernate-orm.database.generation=update
|
||||
|
||||
# Docker
|
||||
## URL của Gitea Registry (thường là gitea.yourdomain.com)
|
||||
quarkus.container-image.registry=git.demonkernel.io.vn
|
||||
quarkus.container-image.group=foodsurf
|
||||
quarkus.container-image.name=account-service
|
||||
|
||||
@@ -77,4 +77,45 @@ public class UserModelTest {
|
||||
|
||||
assertEquals("InvalidPhoneNumber", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testLoginSuccess() {
|
||||
//dang nhap thanh cong
|
||||
TestUserEntity user = new TestUserEntity();
|
||||
String phone =
|
||||
"+8477" + String.format("%07d", (new Random()).nextInt(10000000));
|
||||
String password = "02092006Danh";
|
||||
user.signup("Tran Huu Danh", phone, password);
|
||||
|
||||
Boolean loginResult = user.login(password);
|
||||
|
||||
assertTrue(loginResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testLoginFailWrongPassword() {
|
||||
//dang nhap that bai
|
||||
TestUserEntity user = new TestUserEntity();
|
||||
String phone =
|
||||
"+8477" + String.format("%07d", (new Random()).nextInt(10000000));
|
||||
String truePassword = "02092006Danh";
|
||||
String wrongPassword = "02092006danhh";
|
||||
|
||||
user.signup("Tran Huu Danh", phone, truePassword);
|
||||
Boolean wrongTest = user.login(wrongPassword);
|
||||
|
||||
assertFalse(wrongTest);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testLoginFailNullHashedPassword() {
|
||||
TestUserEntity user = new TestUserEntity();
|
||||
user.hashedPassword = null;
|
||||
|
||||
boolean loginResult = user.login("anhdomixi");
|
||||
assertFalse(loginResult);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.dtos.*;
|
||||
import com.drinkool.entities.CustomerEntity;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.http.ContentType;
|
||||
import java.util.Random;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
public class AuthenticationServiceTest {
|
||||
|
||||
private Random random = new Random();
|
||||
|
||||
private String generateRandomPhone() {
|
||||
return "+8477" + String.format("%07d", random.nextInt(10000000));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSignup() {
|
||||
String phone = generateRandomPhone();
|
||||
Signup input = new Signup();
|
||||
input.name = "Test User";
|
||||
input.phone = phone;
|
||||
input.password = "password123";
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(input)
|
||||
.when()
|
||||
.post(Url.Signup)
|
||||
.then()
|
||||
.statusCode(201);
|
||||
|
||||
// Kiểm tra database: Phải tìm thấy 1 user với số điện thoại này
|
||||
assertEquals(1, CustomerEntity.find("phone", phone).count());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testQuickSignup() {
|
||||
String phone = generateRandomPhone();
|
||||
QuickSignup input = new QuickSignup();
|
||||
input.phone = phone;
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(input)
|
||||
.when()
|
||||
.post(Url.QuickSignup)
|
||||
.then()
|
||||
.statusCode(201);
|
||||
|
||||
assertEquals(1, CustomerEntity.find("phone", phone).count());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccess() {
|
||||
// BƯỚC 1: Tạo user trước bằng signup
|
||||
String phone = generateRandomPhone();
|
||||
Signup signupData = new Signup();
|
||||
signupData.name = "Login User";
|
||||
signupData.phone = phone;
|
||||
signupData.password = "secure_pass";
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(signupData)
|
||||
.post(Url.Signup)
|
||||
.then()
|
||||
.statusCode(201);
|
||||
|
||||
// BƯỚC 2: Thử đăng nhập
|
||||
Login loginInput = new Login();
|
||||
loginInput.phone = phone;
|
||||
loginInput.password = "secure_pass";
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(loginInput)
|
||||
.when()
|
||||
.post(Url.Login)
|
||||
.then()
|
||||
.statusCode(202);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginFailWrongPassword() {
|
||||
String phone = generateRandomPhone();
|
||||
Signup signupData = new Signup();
|
||||
signupData.name = "Wrong Pass User";
|
||||
signupData.phone = phone;
|
||||
signupData.password = "correct_password";
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(signupData)
|
||||
.post(Url.Signup)
|
||||
.then()
|
||||
.statusCode(201);
|
||||
|
||||
Login loginInput = new Login();
|
||||
loginInput.phone = phone;
|
||||
loginInput.password = "wrong_password";
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(loginInput)
|
||||
.when()
|
||||
.post(Url.Login)
|
||||
.then()
|
||||
.statusCode(400); // BadRequestException
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSmsOtp() {
|
||||
String phone = generateRandomPhone();
|
||||
SmsOtp input = new SmsOtp();
|
||||
input.phone = phone;
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(input)
|
||||
.when()
|
||||
.post(Url.SmsOtp)
|
||||
.then()
|
||||
.statusCode(202);
|
||||
|
||||
// Lưu ý: Vì không dùng Mock, code sẽ chạy qua OtpService thật.
|
||||
// Nếu OtpService lưu vào DB, bạn có thể bổ sung Assert tại đây để check OtpEntity.
|
||||
}
|
||||
|
||||
@Test
|
||||
void testQuickLoginFailInvalidOtp() {
|
||||
String phone = generateRandomPhone();
|
||||
QuickLogin input = new QuickLogin();
|
||||
input.phone = phone;
|
||||
input.otp = "000000"; // Giả sử mã này luôn sai vì chưa được tạo
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(input)
|
||||
.when()
|
||||
.post(Url.QuickLogin)
|
||||
.then()
|
||||
.statusCode(401);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
# Quarkus
|
||||
quarkus.http.host=0.0.0.0
|
||||
|
||||
# Database
|
||||
## Datasource
|
||||
%test.quarkus.datasource.db-kind=h2
|
||||
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:test_db;DB_CLOSE_DELAY=-1
|
||||
|
||||
## Orm
|
||||
%test.quarkus.hibernate-orm.database.generation=drop-and-create
|
||||
|
||||
# Docker
|
||||
%test.quarkus.container-image.build=false
|
||||
%test.quarkus.container-image.push=false
|
||||
@@ -19,8 +19,6 @@ public class MenuItemEntity extends BaseEntity {
|
||||
)
|
||||
public List<MenuItemIngredientEntity> ingredients = new ArrayList<>();
|
||||
|
||||
private MenuItemEntity() {}
|
||||
|
||||
/**
|
||||
* Thêm nguyên liệu vào món ăn với định lượng cụ thể
|
||||
*/
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
quarkus.http.host=0.0.0.0
|
||||
|
||||
# Database
|
||||
# Database
|
||||
## Datasource
|
||||
%test.quarkus.datasource.db-kind=h2
|
||||
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:test_db;DB_CLOSE_DELAY=-1
|
||||
|
||||
## Orm
|
||||
%test.quarkus.hibernate-orm.database.generation=drop-and-create
|
||||
|
||||
@@ -48,10 +48,6 @@
|
||||
<artifactId>lib</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-redis-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-rest-jackson</artifactId>
|
||||
@@ -64,6 +60,10 @@
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-rest</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-container-image-docker</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-rest-client-jackson</artifactId>
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.drinkool;
|
||||
|
||||
import com.drinkool.dtos.*;
|
||||
import com.drinkool.services.AccountService;
|
||||
import io.quarkus.redis.datasource.RedisDataSource;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
@@ -16,28 +15,14 @@ public class CustomerGatewayResource {
|
||||
@RestClient
|
||||
AccountService accountService;
|
||||
|
||||
@Inject
|
||||
RedisDataSource redisDataSource;
|
||||
|
||||
@POST
|
||||
@Path("/signup")
|
||||
@Path(Url.Signup)
|
||||
public Uni<Response> proxySignup(Signup input) {
|
||||
String otpVerifiedKey = "otp:verified:" + input.phone;
|
||||
var valueCmd = redisDataSource.value(String.class);
|
||||
|
||||
if (valueCmd.get(otpVerifiedKey) == null) {
|
||||
return Uni.createFrom().item(
|
||||
Response.status(Response.Status.FORBIDDEN)
|
||||
.entity("Phone not verified")
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
return accountService.signup(input);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/login")
|
||||
@Path(Url.Login)
|
||||
public Uni<Response> proxyLogin(Login input) {
|
||||
return accountService.login(input);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.dtos.*;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.*;
|
||||
@@ -7,17 +8,25 @@ import jakarta.ws.rs.core.Response;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@RegisterRestClient(configKey = "account")
|
||||
@Path("/api")
|
||||
@Path("")
|
||||
public interface AccountService {
|
||||
@POST
|
||||
@Path("/signup")
|
||||
@Path(Url.Signup)
|
||||
Uni<Response> signup(Signup input);
|
||||
|
||||
@POST
|
||||
@Path("/quickSignup")
|
||||
@Path(Url.QuickSignup)
|
||||
Uni<Response> quickSignup(QuickSignup input);
|
||||
|
||||
@POST
|
||||
@Path("/login")
|
||||
@Path(Url.Login)
|
||||
Uni<Response> login(Login input);
|
||||
|
||||
@POST
|
||||
@Path(Url.QuickLogin)
|
||||
Uni<Response> quickLogin(QuickSignup input);
|
||||
|
||||
@POST
|
||||
@Path(Url.SmsOtp)
|
||||
Uni<Response> smsOtp(SmsOtp input);
|
||||
}
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
quarkus.rest-client.account.url=http://localhost:502
|
||||
# Docker
|
||||
quarkus.container-image.registry=git.demonkernel.io.vn
|
||||
quarkus.container-image.group=foodsurf
|
||||
quarkus.container-image.name=gateway-service
|
||||
quarkus.container-image.push=true
|
||||
quarkus.container-image.build=true
|
||||
quarkus.container-image.builder=docker
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
# 1. ACCOUNT SERVICE
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: account-service
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: account-service
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: account-service
|
||||
spec:
|
||||
containers:
|
||||
- name: account-service
|
||||
image: git.demonkernel.io.vn/foodsurf/account-service:latest
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: QUARKUS_REDIS_HOST
|
||||
value: "redis-service"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: account-service
|
||||
spec:
|
||||
selector:
|
||||
app: account-service
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
---
|
||||
# 2. GATEWAY SERVICE
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: gateway-deploy
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: gateway
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: gateway
|
||||
spec:
|
||||
containers:
|
||||
- name: gateway
|
||||
image: git.demonkernel.io.vn/foodsurf/gateway-service:latest
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: QUARKUS_REST_CLIENT_ACCOUNT_URL
|
||||
value: "http://account-service"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: gateway-service
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: gateway
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
nodePort: 39080
|
||||
---
|
||||
# 3. REDIS
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: redis-deploy
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:alpine
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
resources:
|
||||
limits:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
requests:
|
||||
cpu: "20m"
|
||||
memory: "64Mi"
|
||||
command:
|
||||
[
|
||||
"redis-server",
|
||||
"--maxmemory",
|
||||
"96mb",
|
||||
"--maxmemory-policy",
|
||||
"allkeys-lru",
|
||||
]
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis-service
|
||||
spec:
|
||||
selector:
|
||||
app: redis
|
||||
ports:
|
||||
- port: 6379
|
||||
+2
-2
@@ -15,8 +15,8 @@
|
||||
|
||||
<artifactId>lib</artifactId>
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.drinkool;
|
||||
|
||||
public class Url {
|
||||
|
||||
public static final String Signup = "/signup";
|
||||
public static final String QuickSignup = "/quick_signup";
|
||||
public static final String Login = "/login";
|
||||
public static final String QuickLogin = "/quick_login";
|
||||
public static final String SmsOtp = "/sms_otp";
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
<version>1.1.0-dev.1</version>
|
||||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
||||
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
||||
<quarkus.platform.version>3.32.3</quarkus.platform.version>
|
||||
|
||||
Reference in New Issue
Block a user