Compare commits

...

2 Commits

Author SHA1 Message Date
TakahashiNguyen c6683b28dc fix: add upload images (#50)
Release package / release (push) Successful in 17m59s
Co-authored-by: TakahashiNg <83152264+TakahashiNguyen@users.noreply.github.com>
Reviewed-on: #50
2026-05-10 14:10:20 +00:00
gitea-actions e963b0f850 chore: release [ci skip] 2026-05-07 15:15:43 +00:00
31 changed files with 431 additions and 30 deletions
+2 -1
View File
@@ -26,7 +26,8 @@ RUN apt-get update --fix-missing && apt-get install --no-install-recommends --no
kubectl \ kubectl \
openjdk-25-jdk-headless \ openjdk-25-jdk-headless \
redis-server \ redis-server \
maven maven \
python3-venv
RUN jbang trust add https://repo1.maven.org/maven2/io/quarkus/quarkus-cli/ RUN jbang trust add https://repo1.maven.org/maven2/io/quarkus/quarkus-cli/
RUN jbang app install --fresh --force quarkus@quarkusio RUN jbang app install --fresh --force quarkus@quarkusio
+3 -1
View File
@@ -23,7 +23,9 @@
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"PeterSchmalfeldt.explorer-exclude", "PeterSchmalfeldt.explorer-exclude",
"redhat.java", "redhat.java",
"ryanluker.vscode-coverage-gutters" "ryanluker.vscode-coverage-gutters",
"ms-python.black-formatter",
"ms-python.python"
] ]
} }
}, },
+2 -1
View File
@@ -8,7 +8,8 @@
"**/target": true, "**/target": true,
"**/docker": true, "**/docker": true,
"node_modules": true, "node_modules": true,
"**/.mvn": true "**/.mvn": true,
"**/venv": true
}, },
"explorerExclude.backup": {}, "explorerExclude.backup": {},
"java.compile.nullAnalysis.mode": "automatic", "java.compile.nullAnalysis.mode": "automatic",
+7
View File
@@ -1,3 +1,10 @@
## [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) ## [1.5.23](https://git.demonkernel.io.vn/FoodSurf/backend/compare/v1.5.22...v1.5.23) (2026-05-07)
+1 -1
View File
@@ -9,7 +9,7 @@
<parent> <parent>
<groupId>com.drinkool</groupId> <groupId>com.drinkool</groupId>
<artifactId>drinkool</artifactId> <artifactId>drinkool</artifactId>
<version>1.5.23</version> <version>1.5.24</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
+1 -11
View File
@@ -9,7 +9,7 @@
<parent> <parent>
<groupId>com.drinkool</groupId> <groupId>com.drinkool</groupId>
<artifactId>drinkool</artifactId> <artifactId>drinkool</artifactId>
<version>1.5.23</version> <version>1.5.24</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@@ -111,16 +111,6 @@
<artifactId>quarkus-maven-plugin</artifactId> <artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version> <version>${quarkus.platform.version}</version>
<extensions>true</extensions> <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>
<plugin> <plugin>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
+1 -1
View File
@@ -80,7 +80,7 @@
# You can find more information about the UBI base runtime images and their configuration here: # You can find more information about the UBI base runtime images and their configuration here:
# https://rh-openjdk.github.io/redhat-openjdk-containers/ # 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' ENV LANGUAGE='en_US:en'
@@ -80,7 +80,7 @@
# You can find more information about the UBI base runtime images and their configuration here: # You can find more information about the UBI base runtime images and their configuration here:
# https://rh-openjdk.github.io/redhat-openjdk-containers/ # 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' 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. # 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`. # 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/ WORKDIR /work/
RUN chown 1001 /work \ RUN chown 1001 /work \
&& chmod "g+rwX" /work \ && chmod "g+rwX" /work \
+1 -1
View File
@@ -9,7 +9,7 @@
<parent> <parent>
<groupId>com.drinkool</groupId> <groupId>com.drinkool</groupId>
<artifactId>drinkool</artifactId> <artifactId>drinkool</artifactId>
<version>1.5.23</version> <version>1.5.24</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@@ -3,12 +3,14 @@ package com.drinkool.dtos;
import com.drinkool.entities.MenuItemEntity; import com.drinkool.entities.MenuItemEntity;
import com.drinkool.entities.ReviewEntity; import com.drinkool.entities.ReviewEntity;
import com.drinkool.lib.dtos.SendReview; import com.drinkool.lib.dtos.SendReview;
import jakarta.enterprise.context.ApplicationScoped;
import org.mapstruct.BeanMapping; import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget; import org.mapstruct.MappingTarget;
import org.mapstruct.NullValuePropertyMappingStrategy; import org.mapstruct.NullValuePropertyMappingStrategy;
@Mapper(componentModel = "cdi") @ApplicationScoped
@Mapper(componentModel = "jakarta")
public interface DtoMapper { public interface DtoMapper {
@BeanMapping( @BeanMapping(
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE
+4
View File
@@ -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
+102
View File
@@ -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>
+32
View File
@@ -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"]
+52
View File
@@ -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")
+5
View File
@@ -0,0 +1,5 @@
fastapi
uvicorn
python-multipart
Pillow
filetype
+3 -3
View File
@@ -9,7 +9,7 @@
<parent> <parent>
<groupId>com.drinkool</groupId> <groupId>com.drinkool</groupId>
<artifactId>drinkool</artifactId> <artifactId>drinkool</artifactId>
<version>1.5.23</version> <version>1.5.24</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@@ -151,8 +151,8 @@
<configuration> <configuration>
<argLine>@{argLine}</argLine> <argLine>@{argLine}</argLine>
<systemPropertyVariables> <systemPropertyVariables>
<native.image.path> <native.image.path
${project.build.directory}/${project.build.finalName}-runner</native.image.path> >${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager <java.util.logging.manager
>org.jboss.logmanager.LogManager</java.util.logging.manager> >org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home> <maven.home>${maven.home}</maven.home>
@@ -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.lib.dtos.Login;
import com.drinkool.services.CustomerService; import com.drinkool.services.CustomerService;
import com.drinkool.services.ManagerService; import com.drinkool.services.ManagerService;
import com.drinkool.services.StaffService;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.ws.rs.POST; import jakarta.ws.rs.POST;
@@ -22,6 +23,10 @@ public class MainController extends BaseController {
@RestClient @RestClient
CustomerService customerService; CustomerService customerService;
@Inject
@RestClient
StaffService staffService;
@POST @POST
@Path(Url.Login) @Path(Url.Login)
public Uni<Response> proxyLogin(Login input) { public Uni<Response> proxyLogin(Login input) {
@@ -29,7 +34,12 @@ public class MainController extends BaseController {
.login(input) .login(input)
.onFailure() .onFailure()
.recoverWithUni(() -> { .recoverWithUni(() -> {
return customerService.login(input); 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);
}
@@ -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 # Rest Client
quarkus.rest-client.account.url=http://account-service quarkus.rest-client.account.url=http://account-service
quarkus.rest-client.eatery.url=http://eatery-service quarkus.rest-client.eatery.url=http://eatery-service
quarkus.rest-client.file.url=http://file-service
# GraphQL Client # GraphQL Client
quarkus.smallrye-graphql-client.eatery.url=http://eatery-service/graphql quarkus.smallrye-graphql-client.eatery.url=http://eatery-service/graphql
+1 -1
View File
@@ -14,7 +14,7 @@ spec:
spec: spec:
containers: containers:
- name: account-pod - name: account-pod
image: git.demonkernel.io.vn/foodsurf/account-service:1.5.23 image: git.demonkernel.io.vn/foodsurf/account-service:1.5.24
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
ports: ports:
- containerPort: 8080 - containerPort: 8080
+1 -1
View File
@@ -14,7 +14,7 @@ spec:
spec: spec:
containers: containers:
- name: cart-pod - name: cart-pod
image: git.demonkernel.io.vn/foodsurf/cart-service:1.5.23 image: git.demonkernel.io.vn/foodsurf/cart-service:1.5.24
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
ports: ports:
- containerPort: 8080 - containerPort: 8080
+1 -1
View File
@@ -14,7 +14,7 @@ spec:
spec: spec:
containers: containers:
- name: eatery-pod - name: eatery-pod
image: git.demonkernel.io.vn/foodsurf/eatery-service:1.5.23 image: git.demonkernel.io.vn/foodsurf/eatery-service:1.5.24
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
ports: ports:
- containerPort: 8080 - containerPort: 8080
+69
View File
@@ -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
+1 -1
View File
@@ -14,7 +14,7 @@ spec:
spec: spec:
containers: containers:
- name: gateway-pod - name: gateway-pod
image: git.demonkernel.io.vn/foodsurf/gateway-service:1.5.23 image: git.demonkernel.io.vn/foodsurf/gateway-service:1.5.24
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
ports: ports:
- containerPort: 8080 - containerPort: 8080
+2
View File
@@ -1,6 +1,8 @@
resources: resources:
- account.yaml - account.yaml
- eatery.yaml - eatery.yaml
- cart.yaml
- file.yaml
- gateway.yaml - gateway.yaml
- redis.yaml - redis.yaml
- kafdrop.yaml - kafdrop.yaml
+1 -1
View File
@@ -9,7 +9,7 @@
<parent> <parent>
<groupId>com.drinkool</groupId> <groupId>com.drinkool</groupId>
<artifactId>drinkool</artifactId> <artifactId>drinkool</artifactId>
<version>1.5.23</version> <version>1.5.24</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
+2 -1
View File
@@ -7,7 +7,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.drinkool</groupId> <groupId>com.drinkool</groupId>
<artifactId>drinkool</artifactId> <artifactId>drinkool</artifactId>
<version>1.5.23</version> <version>1.5.24</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<properties> <properties>
<maven.compiler.source>25</maven.compiler.source> <maven.compiler.source>25</maven.compiler.source>
@@ -23,6 +23,7 @@
<module>account-service</module> <module>account-service</module>
<module>gateway-service</module> <module>gateway-service</module>
<module>cart-service</module> <module>cart-service</module>
<module>file-service</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>