Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6683b28dc | |||
| e963b0f850 | |||
| 71575ec2ad | |||
| bd548278b4 | |||
| da6f3295dd | |||
| 0d9e13899b |
@@ -26,7 +26,8 @@ RUN apt-get update --fix-missing && apt-get install --no-install-recommends --no
|
||||
kubectl \
|
||||
openjdk-25-jdk-headless \
|
||||
redis-server \
|
||||
maven
|
||||
maven \
|
||||
python3-venv
|
||||
|
||||
RUN jbang trust add https://repo1.maven.org/maven2/io/quarkus/quarkus-cli/
|
||||
RUN jbang app install --fresh --force quarkus@quarkusio
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
"esbenp.prettier-vscode",
|
||||
"PeterSchmalfeldt.explorer-exclude",
|
||||
"redhat.java",
|
||||
"ryanluker.vscode-coverage-gutters"
|
||||
"ryanluker.vscode-coverage-gutters",
|
||||
"ms-python.black-formatter",
|
||||
"ms-python.python"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
Vendored
+3
-1
@@ -7,7 +7,9 @@
|
||||
"**/Thumbs.db": true,
|
||||
"**/target": true,
|
||||
"**/docker": true,
|
||||
"node_modules": true
|
||||
"node_modules": true,
|
||||
"**/.mvn": true,
|
||||
"**/venv": true
|
||||
},
|
||||
"explorerExclude.backup": {},
|
||||
"java.compile.nullAnalysis.mode": "automatic",
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
## [1.5.24](https://git.demonkernel.io.vn/FoodSurf/backend/compare/v1.5.23...v1.5.24) (2026-05-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add staff entity ([#48](https://git.demonkernel.io.vn/FoodSurf/backend/issues/48)) ([71575ec](https://git.demonkernel.io.vn/FoodSurf/backend/commit/71575ec2ad5fbb402ec9ad17caba4314e0dd0454))
|
||||
|
||||
## [1.5.23](https://git.demonkernel.io.vn/FoodSurf/backend/compare/v1.5.22...v1.5.23) (2026-05-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* resolved image issue ([#47](https://git.demonkernel.io.vn/FoodSurf/backend/issues/47)) ([da6f329](https://git.demonkernel.io.vn/FoodSurf/backend/commit/da6f3295ddc8f34836ae7009c2c76e18e414b4df))
|
||||
|
||||
## [1.5.22](https://git.demonkernel.io.vn/FoodSurf/backend/compare/v1.5.21...v1.5.22) (2026-05-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add cart service ([#46](https://git.demonkernel.io.vn/FoodSurf/backend/issues/46)) ([bb828d7](https://git.demonkernel.io.vn/FoodSurf/backend/commit/bb828d7532fb6f849c247376e339c08f0759f91d))
|
||||
|
||||
## [1.5.21](https://git.demonkernel.io.vn/FoodSurf/backend/compare/v1.5.20...v1.5.21) (2026-05-07)
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.21</version>
|
||||
<version>1.5.24</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -54,6 +54,12 @@
|
||||
<artifactId>jose4j</artifactId>
|
||||
<version>0.9.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.datafaker</groupId>
|
||||
<artifactId>datafaker</artifactId>
|
||||
<version>2.0.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-messaging-kafka</artifactId>
|
||||
|
||||
@@ -3,11 +3,20 @@ package com.drinkool.entities;
|
||||
import com.drinkool.Role;
|
||||
import com.drinkool.models.UserModel;
|
||||
import jakarta.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = Role.Manager)
|
||||
public class ManagerEntity extends UserModel {
|
||||
|
||||
@OneToMany(
|
||||
mappedBy = "serve",
|
||||
cascade = CascadeType.ALL,
|
||||
orphanRemoval = true
|
||||
)
|
||||
public List<StaffEntity> staffs = new ArrayList<>();
|
||||
|
||||
public ManagerEntity() {
|
||||
super(Role.Manager);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.drinkool.entities;
|
||||
|
||||
import com.drinkool.Role;
|
||||
import com.drinkool.models.UserModel;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
@Entity
|
||||
@Table(name = Role.Staff)
|
||||
public class StaffEntity extends UserModel {
|
||||
|
||||
@Transactional
|
||||
public void signup(
|
||||
String name,
|
||||
String phone,
|
||||
String rawPassword,
|
||||
ManagerEntity serve
|
||||
) {
|
||||
this.serve = serve;
|
||||
super.signup(name, phone, rawPassword);
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "managerId")
|
||||
public ManagerEntity serve;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.drinkool.filters;
|
||||
|
||||
import com.drinkool.lib.utils.BaseHeaderAuthentication;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class HeaderRolesFilter extends BaseHeaderAuthentication {}
|
||||
@@ -3,6 +3,7 @@ package com.drinkool.services;
|
||||
import com.drinkool.Role;
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.entities.ManagerEntity;
|
||||
import com.drinkool.entities.StaffEntity;
|
||||
import com.drinkool.lib.dtos.SmsOtp;
|
||||
import com.drinkool.models.UserModel;
|
||||
import jakarta.inject.Inject;
|
||||
@@ -28,6 +29,10 @@ public class AccountService {
|
||||
ManagerEntity.find("phone", input.phone).firstResult() != null
|
||||
) return Response.accepted().entity(Role.Manager).build();
|
||||
|
||||
if (
|
||||
StaffEntity.find("phone", input.phone).firstResult() != null
|
||||
) return Response.accepted().entity(Role.Staff).build();
|
||||
|
||||
String otp = otpService.generateAndSaveOtp(input.phone);
|
||||
|
||||
System.out.println("OTP cho " + input.phone + " là: " + otp);
|
||||
|
||||
@@ -44,25 +44,25 @@ public class ManagerService extends BaseService<ManagerEntity> {
|
||||
@POST
|
||||
@Path(Url.Login)
|
||||
public Response login(Login input) {
|
||||
ManagerEntity customer = ManagerEntity.find(
|
||||
ManagerEntity manager = ManagerEntity.find(
|
||||
"phone",
|
||||
input.phone
|
||||
).firstResult();
|
||||
|
||||
if (customer == null) return Response.status(Response.Status.BAD_REQUEST)
|
||||
if (manager == null) return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("InvalidPhoneNumber")
|
||||
.build();
|
||||
|
||||
if (!customer.login(input.password)) return Response.status(
|
||||
if (!manager.login(input.password)) return Response.status(
|
||||
Response.Status.BAD_REQUEST
|
||||
)
|
||||
.entity("InvalidPassword")
|
||||
.build();
|
||||
|
||||
return Response.accepted()
|
||||
.header(InternalValue.userId, customer.id.toString())
|
||||
.header(InternalValue.userId, manager.id.toString())
|
||||
.header(InternalValue.role, Role.Manager)
|
||||
.entity(customer)
|
||||
.entity(manager)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import com.drinkool.InternalValue;
|
||||
import com.drinkool.Role;
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.entities.ManagerEntity;
|
||||
import com.drinkool.entities.StaffEntity;
|
||||
import com.drinkool.lib.dtos.Login;
|
||||
import com.drinkool.lib.dtos.Signup;
|
||||
import io.vertx.core.http.HttpServerRequest;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.UUID;
|
||||
|
||||
@Path(Role.Staff)
|
||||
public class StaffService extends BaseService<StaffEntity> {
|
||||
|
||||
@Inject
|
||||
HttpServerRequest request;
|
||||
|
||||
public StaffService() {
|
||||
super(StaffEntity.class);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path(Url.Signup)
|
||||
@RolesAllowed(Role.Manager)
|
||||
@Transactional
|
||||
public Response signup(Signup input) {
|
||||
UUID managerId = UUID.fromString(request.getHeader(InternalValue.userId));
|
||||
ManagerEntity manager = ManagerEntity.findById(managerId);
|
||||
|
||||
if (manager == null) return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("InvalidManager")
|
||||
.build();
|
||||
|
||||
StaffEntity staff = new StaffEntity();
|
||||
staff.signup(input.name, input.phone, input.password, manager);
|
||||
|
||||
return Response.status(Response.Status.CREATED)
|
||||
.header(InternalValue.userId, staff.id.toString())
|
||||
.header(InternalValue.role, Role.Staff)
|
||||
.build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path(Url.Login)
|
||||
public Response login(Login input) {
|
||||
StaffEntity staff = StaffEntity.find("phone", input.phone).firstResult();
|
||||
|
||||
if (staff == null) return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("InvalidPhoneNumber")
|
||||
.build();
|
||||
|
||||
if (!staff.login(input.password)) return Response.status(
|
||||
Response.Status.BAD_REQUEST
|
||||
)
|
||||
.entity("InvalidPassword")
|
||||
.build();
|
||||
|
||||
return Response.accepted()
|
||||
.header(InternalValue.userId, staff.id.toString())
|
||||
.header(InternalValue.role, Role.Staff)
|
||||
.entity(staff)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import static io.restassured.RestAssured.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import com.drinkool.*;
|
||||
import com.drinkool.entities.StaffEntity;
|
||||
import com.drinkool.lib.dtos.*;
|
||||
import io.quarkus.test.common.QuarkusTestResource;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.kafka.*;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.smallrye.reactive.messaging.kafka.companion.ConsumerTask;
|
||||
import io.smallrye.reactive.messaging.kafka.companion.KafkaCompanion;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.time.Duration;
|
||||
import net.datafaker.Faker;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
@QuarkusTestResource(KafkaCompanionResource.class)
|
||||
public class StaffServiceTest {
|
||||
|
||||
private final Faker faker = new Faker();
|
||||
|
||||
private String managerId;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
ManagerSignup input = new ManagerSignup();
|
||||
input.name = "Ho Van Quan";
|
||||
input.phone = Utils.generateRandomPhone();
|
||||
input.password = "strong_password";
|
||||
input.eateryName = "Quan Com Ngon";
|
||||
|
||||
var response = given()
|
||||
.contentType("application/json")
|
||||
.body(input)
|
||||
.when()
|
||||
.post(Role.Manager + Url.Signup)
|
||||
.then()
|
||||
.statusCode(201)
|
||||
.header(InternalValue.userId, org.hamcrest.Matchers.notNullValue())
|
||||
.header(InternalValue.role, Role.Manager)
|
||||
.extract();
|
||||
|
||||
managerId = response.header(InternalValue.userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void signup_Success() {
|
||||
String staffName = faker.name().fullName();
|
||||
String staffPhone = Utils.generateRandomPhone();
|
||||
|
||||
Signup signup = new Signup(
|
||||
staffName,
|
||||
staffPhone,
|
||||
faker.famousLastWords().lastWords()
|
||||
);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.header(InternalValue.role, Role.Manager)
|
||||
.header(InternalValue.userId, managerId)
|
||||
.body(signup)
|
||||
.when()
|
||||
.post(Role.Staff + Url.Signup)
|
||||
.then()
|
||||
.statusCode(Response.Status.CREATED.getStatusCode())
|
||||
.header(InternalValue.userId, org.hamcrest.Matchers.notNullValue())
|
||||
.header(InternalValue.role, Role.Staff);
|
||||
|
||||
StaffEntity staff = StaffEntity.find("phone", staffPhone).firstResult();
|
||||
|
||||
assertEquals(managerId, staff.serve.id.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void signup_FailedDueToInvalidRole() {
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.header(InternalValue.role, Role.Customer)
|
||||
.header(InternalValue.userId, managerId)
|
||||
.body("")
|
||||
.when()
|
||||
.post(Role.Staff + Url.Signup)
|
||||
.then()
|
||||
.statusCode(Response.Status.FORBIDDEN.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccess() {
|
||||
String staffName = faker.name().fullName();
|
||||
String staffPhone = Utils.generateRandomPhone();
|
||||
String password = faker.famousLastWords().lastWords();
|
||||
|
||||
Signup signup = new Signup(staffName, staffPhone, password);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.header(InternalValue.role, Role.Manager)
|
||||
.header(InternalValue.userId, managerId)
|
||||
.body(signup)
|
||||
.when()
|
||||
.post(Role.Staff + Url.Signup)
|
||||
.then();
|
||||
|
||||
Login loginInput = new Login(staffPhone, password);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(loginInput)
|
||||
.when()
|
||||
.post(Role.Staff + Url.Login)
|
||||
.then()
|
||||
.statusCode(Response.Status.ACCEPTED.getStatusCode());
|
||||
}
|
||||
}
|
||||
+2
-11
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.21</version>
|
||||
<version>1.5.24</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -111,16 +111,6 @@
|
||||
<artifactId>quarkus-maven-plugin</artifactId>
|
||||
<version>${quarkus.platform.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
<goal>generate-code</goal>
|
||||
<goal>generate-code-tests</goal>
|
||||
<goal>native-image-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
@@ -181,6 +171,7 @@
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<quarkus.package.jar.enabled>false</quarkus.package.jar.enabled>
|
||||
<skipITs>false</skipITs>
|
||||
<quarkus.native.enabled>true</quarkus.native.enabled>
|
||||
</properties>
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
# You can find more information about the UBI base runtime images and their configuration here:
|
||||
# https://rh-openjdk.github.io/redhat-openjdk-containers/
|
||||
###
|
||||
FROM registry.access.redhat.com/ubi8/openjdk-21:1.23
|
||||
FROM registry.access.redhat.com/ubi9/openjdk-25:1.24
|
||||
|
||||
ENV LANGUAGE='en_US:en'
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
# You can find more information about the UBI base runtime images and their configuration here:
|
||||
# https://rh-openjdk.github.io/redhat-openjdk-containers/
|
||||
###
|
||||
FROM registry.access.redhat.com/ubi8/openjdk-21:1.23
|
||||
FROM registry.access.redhat.com/ubi9/openjdk-25:1.24
|
||||
|
||||
ENV LANGUAGE='en_US:en'
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
# The `quay.io/quarkus/quarkus-micro-image:2.0` base image is based on UBI 9.
|
||||
# To use UBI 8, switch to `quay.io/quarkus/quarkus-micro-image:2.0`.
|
||||
###
|
||||
FROM quay.io/quarkus/quarkus-micro-image:2.0
|
||||
FROM quay.io/quarkus/ubi9-quarkus-micro-image:2.0
|
||||
WORKDIR /work/
|
||||
RUN chown 1001 /work \
|
||||
&& chmod "g+rwX" /work \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Docker
|
||||
quarkus.container-image.registry=vps.demonkernel.io.vn
|
||||
quarkus.container-image.group=foodsurf
|
||||
quarkus.container-image.name=gateway-service
|
||||
quarkus.container-image.name=cart-service
|
||||
quarkus.container-image.push=true
|
||||
quarkus.container-image.build=true
|
||||
quarkus.container-image.builder=docker
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.21</version>
|
||||
<version>1.5.24</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -3,12 +3,14 @@ package com.drinkool.dtos;
|
||||
import com.drinkool.entities.MenuItemEntity;
|
||||
import com.drinkool.entities.ReviewEntity;
|
||||
import com.drinkool.lib.dtos.SendReview;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.NullValuePropertyMappingStrategy;
|
||||
|
||||
@Mapper(componentModel = "cdi")
|
||||
@ApplicationScoped
|
||||
@Mapper(componentModel = "jakarta")
|
||||
public interface DtoMapper {
|
||||
@BeanMapping(
|
||||
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
wrapperVersion=3.3.4
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.15/apache-maven-3.9.15-bin.zip
|
||||
distributionSha256Sum=b0d9292f06c5faded31ddcb6cb69099f316d6fea40a7778624b259bad9fed18a
|
||||
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.24</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>file-service</artifactId>
|
||||
|
||||
<properties>
|
||||
<quarkus.container-image.registry>vps.demonkernel.io.vn</quarkus.container-image.registry>
|
||||
<quarkus.container-image.group>foodsurf</quarkus.container-image.group>
|
||||
<quarkus.container-image.name>file-service</quarkus.container-image.name>
|
||||
<quarkus.container-image.push>true</quarkus.container-image.push>
|
||||
|
||||
<python.image.full.name>
|
||||
${quarkus.container-image.registry}/${quarkus.container-image.group}/${quarkus.container-image.name}</python.image.full.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-container-image-docker</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>docker-build-python</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>docker</executable>
|
||||
<workingDirectory>${project.basedir}/python</workingDirectory>
|
||||
<arguments>
|
||||
<argument>build</argument>
|
||||
<argument>-t</argument>
|
||||
<argument>${python.image.full.name}:${parent.version}</argument>
|
||||
<argument>-t</argument>
|
||||
<argument>${python.image.full.name}:latest</argument>
|
||||
<argument>.</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>push-python-image</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>quarkus.container-image.push</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>docker-push-python</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>docker</executable>
|
||||
<arguments>
|
||||
<argument>push</argument>
|
||||
<argument>${python.image.full.name}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -0,0 +1,32 @@
|
||||
# --- Stage 1: Build (Cài đặt thư viện) ---
|
||||
FROM python:3.9-slim AS builder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Khởi tạo môi trường ảo để tách biệt thư viện
|
||||
RUN python -m venv /opt/venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --upgrade --no-cache-dir -r requirements.txt
|
||||
|
||||
|
||||
# --- Stage 2: Runtime (Chỉ chạy Prod) ---
|
||||
FROM python:3.9-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Chỉ copy môi trường ảo đã build xong từ Stage 1
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
# Copy mã nguồn
|
||||
COPY main.py .
|
||||
|
||||
# Tạo folder lưu ảnh và phân quyền (tăng tính bảo mật)
|
||||
RUN mkdir images && chmod 777 images
|
||||
|
||||
# Chạy bằng Uvicorn với chế độ production (tắt reload)
|
||||
# Port 80 theo yêu cầu trước đó của bạn
|
||||
EXPOSE 80
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80", "--workers", "4"]
|
||||
@@ -0,0 +1,52 @@
|
||||
import uuid
|
||||
import io
|
||||
from fastapi import FastAPI, Request, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
import filetype
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
STORAGE_DIR = Path("images")
|
||||
STORAGE_DIR.mkdir(exist_ok=True)
|
||||
|
||||
|
||||
@app.post("/")
|
||||
async def upload_image(request: Request):
|
||||
body = await request.body()
|
||||
|
||||
if not body:
|
||||
raise HTTPException(status_code=400, detail="Dữ liệu trống")
|
||||
|
||||
kind = filetype.guess(body)
|
||||
if kind is None or not kind.mime.startswith("image/"):
|
||||
mime_type = kind.mime if kind else "không xác định"
|
||||
raise HTTPException(
|
||||
status_code=400, detail=f"Định dạng {mime_type} không được hỗ trợ"
|
||||
)
|
||||
|
||||
try:
|
||||
img = Image.open(io.BytesIO(body))
|
||||
|
||||
random_filename = f"{uuid.uuid4()}.webp"
|
||||
file_path = STORAGE_DIR / random_filename
|
||||
|
||||
img.save(file_path, format="WEBP", quality=80)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"filename": random_filename,
|
||||
"original_type": kind.mime,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Lỗi xử lý ảnh: {str(e)}")
|
||||
|
||||
|
||||
@app.get("/{name}")
|
||||
async def get_image(name: str):
|
||||
file_path = STORAGE_DIR / name
|
||||
if not file_path.exists():
|
||||
raise HTTPException(status_code=404, detail="NotFoundImage")
|
||||
return FileResponse(file_path, media_type="image/webp")
|
||||
@@ -0,0 +1,5 @@
|
||||
fastapi
|
||||
uvicorn
|
||||
python-multipart
|
||||
Pillow
|
||||
filetype
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.21</version>
|
||||
<version>1.5.24</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -151,8 +151,8 @@
|
||||
<configuration>
|
||||
<argLine>@{argLine}</argLine>
|
||||
<systemPropertyVariables>
|
||||
<native.image.path>
|
||||
${project.build.directory}/${project.build.finalName}-runner</native.image.path>
|
||||
<native.image.path
|
||||
>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
|
||||
<java.util.logging.manager
|
||||
>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||
<maven.home>${maven.home}</maven.home>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.drinkool.controller;
|
||||
|
||||
import com.drinkool.InternalValue;
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.enums.SortBy;
|
||||
import com.drinkool.lib.dtos.GraphqlDto;
|
||||
@@ -9,6 +10,7 @@ import io.smallrye.mutiny.Uni;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.HeaderParam;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
@@ -43,8 +45,12 @@ public class EateryController extends GraphqlBase {
|
||||
|
||||
@POST
|
||||
@Path(Url.Review)
|
||||
public Uni<Response> receiveReview(SendReview input) {
|
||||
return reviewService.receiveReview(input);
|
||||
public Uni<Response> receiveReview(
|
||||
SendReview input,
|
||||
@HeaderParam(InternalValue.userId) UUID userId,
|
||||
@HeaderParam(InternalValue.role) String role
|
||||
) {
|
||||
return reviewService.receiveReview(input, userId, role);
|
||||
}
|
||||
|
||||
@POST
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.drinkool.controller;
|
||||
|
||||
import com.drinkool.services.FileService;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.io.File;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
import org.jboss.resteasy.reactive.RestForm;
|
||||
|
||||
@Path("/api/file")
|
||||
public class FileController {
|
||||
|
||||
@Inject
|
||||
@RestClient
|
||||
FileService fileService;
|
||||
|
||||
@POST
|
||||
public Uni<Response> upload(@RestForm File file) {
|
||||
return fileService.upload(file);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{name}")
|
||||
public Uni<Response> get(@PathParam("name") String name) {
|
||||
return fileService.get(name);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.drinkool.Url;
|
||||
import com.drinkool.lib.dtos.Login;
|
||||
import com.drinkool.services.CustomerService;
|
||||
import com.drinkool.services.ManagerService;
|
||||
import com.drinkool.services.StaffService;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.POST;
|
||||
@@ -22,14 +23,23 @@ public class MainController extends BaseController {
|
||||
@RestClient
|
||||
CustomerService customerService;
|
||||
|
||||
@Inject
|
||||
@RestClient
|
||||
StaffService staffService;
|
||||
|
||||
@POST
|
||||
@Path(Url.Login)
|
||||
public Uni<Response> proxyLogin(Login input) {
|
||||
return managerService
|
||||
.login(input)
|
||||
.onFailure()
|
||||
.recoverWithUni(() -> {
|
||||
return staffService
|
||||
.login(input)
|
||||
.onFailure()
|
||||
.recoverWithUni(() -> {
|
||||
return customerService.login(input);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.drinkool.controller;
|
||||
|
||||
import com.drinkool.InternalValue;
|
||||
import com.drinkool.Role;
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.lib.dtos.Signup;
|
||||
import com.drinkool.services.StaffService;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.HeaderParam;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
|
||||
@Path("/api/" + Role.Staff)
|
||||
public class StaffController {
|
||||
|
||||
@Inject
|
||||
@RestClient
|
||||
StaffService staffService;
|
||||
|
||||
@POST
|
||||
@Path(Url.Signup)
|
||||
public Uni<Response> signup(
|
||||
Signup input,
|
||||
@HeaderParam(InternalValue.userId) UUID userId,
|
||||
@HeaderParam(InternalValue.role) String role
|
||||
) {
|
||||
return staffService.signup(input, userId, role);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
import org.jboss.resteasy.reactive.RestForm;
|
||||
|
||||
@RegisterRestClient(configKey = "file")
|
||||
@Path("/")
|
||||
public interface FileService {
|
||||
@POST
|
||||
Uni<Response> upload(@RestForm File file);
|
||||
|
||||
@GET
|
||||
@Path("/{name}")
|
||||
Uni<Response> get(@PathParam("name") String name);
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import com.drinkool.InternalValue;
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.enums.SortBy;
|
||||
import com.drinkool.lib.dtos.SendReview;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.HeaderParam;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
@@ -25,5 +27,9 @@ public interface ReviewService {
|
||||
);
|
||||
|
||||
@POST
|
||||
public Uni<Response> receiveReview(SendReview input);
|
||||
public Uni<Response> receiveReview(
|
||||
SendReview input,
|
||||
@HeaderParam(InternalValue.userId) UUID userId,
|
||||
@HeaderParam(InternalValue.role) String role
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import com.drinkool.InternalValue;
|
||||
import com.drinkool.Role;
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.lib.dtos.*;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.*;
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
@RegisterRestClient(configKey = "account")
|
||||
@Path(Role.Staff)
|
||||
public interface StaffService {
|
||||
@POST
|
||||
@Path(Url.Signup)
|
||||
Uni<Response> signup(
|
||||
Signup input,
|
||||
@HeaderParam(InternalValue.userId) UUID userId,
|
||||
@HeaderParam(InternalValue.role) String role
|
||||
);
|
||||
|
||||
@POST
|
||||
@Path(Url.Login)
|
||||
Uni<Response> login(Login input);
|
||||
|
||||
@GET
|
||||
@Path(Url.Me)
|
||||
Uni<Response> me(@HeaderParam(InternalValue.userId) UUID id);
|
||||
}
|
||||
@@ -15,6 +15,7 @@ quarkus.native.additional-build-args=--future-defaults=all
|
||||
# Rest Client
|
||||
quarkus.rest-client.account.url=http://account-service
|
||||
quarkus.rest-client.eatery.url=http://eatery-service
|
||||
quarkus.rest-client.file.url=http://file-service
|
||||
|
||||
# GraphQL Client
|
||||
quarkus.smallrye-graphql-client.eatery.url=http://eatery-service/graphql
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: account-pod
|
||||
image: git.demonkernel.io.vn/foodsurf/account-service:1.5.21
|
||||
image: git.demonkernel.io.vn/foodsurf/account-service:1.5.24
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: cart-pod
|
||||
image: git.demonkernel.io.vn/foodsurf/cart-service:1.5.21
|
||||
image: git.demonkernel.io.vn/foodsurf/cart-service:1.5.24
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: eatery-pod
|
||||
image: git.demonkernel.io.vn/foodsurf/eatery-service:1.5.21
|
||||
image: git.demonkernel.io.vn/foodsurf/eatery-service:1.5.24
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: file-deploy
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: file
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: file
|
||||
spec:
|
||||
containers:
|
||||
- name: file-pod
|
||||
image: git.demonkernel.io.vn/foodsurf/file-service:1.5.24
|
||||
imagePullPolicy: IfNotPresent
|
||||
volumeMounts:
|
||||
- name: file-volume
|
||||
mountPath: /app/images
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "150m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "450m"
|
||||
volumes:
|
||||
- name: file-volume
|
||||
persistentVolumeClaim:
|
||||
claimName: file-pvc
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: file-service
|
||||
spec:
|
||||
selector:
|
||||
app: file
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: file-pv
|
||||
spec:
|
||||
capacity:
|
||||
storage: 10Gi
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
nfs:
|
||||
server: 192.168.1.100
|
||||
path: /data/images
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: file-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: gateway-pod
|
||||
image: git.demonkernel.io.vn/foodsurf/gateway-service:1.5.21
|
||||
image: git.demonkernel.io.vn/foodsurf/gateway-service:1.5.24
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
resources:
|
||||
- account.yaml
|
||||
- eatery.yaml
|
||||
- cart.yaml
|
||||
- file.yaml
|
||||
- gateway.yaml
|
||||
- redis.yaml
|
||||
- kafdrop.yaml
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.21</version>
|
||||
<version>1.5.24</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -4,4 +4,5 @@ public class Role {
|
||||
|
||||
public static final String Customer = "customer";
|
||||
public static final String Manager = "manager";
|
||||
public static final String Staff = "Staff";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.drinkool.lib.dtos;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Login {
|
||||
|
||||
public String phone;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.drinkool.lib.dtos;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Signup {
|
||||
|
||||
public String name;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.21</version>
|
||||
<version>1.5.24</version>
|
||||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<maven.compiler.source>25</maven.compiler.source>
|
||||
@@ -23,6 +23,7 @@
|
||||
<module>account-service</module>
|
||||
<module>gateway-service</module>
|
||||
<module>cart-service</module>
|
||||
<module>file-service</module>
|
||||
</modules>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
Reference in New Issue
Block a user