Bugfix/testing (#4)
Build Docker Image / build-backend (push) Successful in 58s
Build Docker Image / build-admin (push) Successful in 6s
Build Docker Image / build-teller (push) Successful in 8s
Build Docker Image / build-customer (push) Successful in 2m6s

Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
2026-07-30 16:22:51 +08:00
parent 318e0c3eb4
commit 0420194541
9 changed files with 179 additions and 117 deletions
+1 -30
View File
@@ -4,7 +4,7 @@ services:
container_name: qms-mysql
restart: unless-stopped
ports:
- "${MYSQL_HOST_PORT:-3307}:3306"
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-password}
MYSQL_DATABASE: ${MYSQL_DATABASE:-qms}
@@ -27,37 +27,8 @@ services:
networks:
- qms-net
backend:
image: ${BACKEND_IMAGE:-qms-backend}:${IMAGE_TAG:-local}
build:
context: .
dockerfile: Dockerfile
container_name: qms-backend
restart: unless-stopped
ports:
# Host port can be changed via BACKEND_HOST_PORT in .env (default 8080)
- "${BACKEND_HOST_PORT:-8080}:8080"
environment:
SERVER_PORT: 8080
# Use Docker service name "mysql", not localhost
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/${MYSQL_DATABASE:-qms}?allowPublicKeyRetrieval=true&useSSL=false
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: ${MYSQL_ROOT_PASSWORD:-password}
JWT_SECRET_KEY: ${JWT_SECRET_KEY:-a68uiaDQ0V3iLjF4DqMuS13GAVwkut55dlFbGCLyXTF}
ADS_UPLOAD_DIR: /app/uploads/ads
NOTIFICATIONS_MOCK: ${NOTIFICATIONS_MOCK:-true}
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-dummy-google-client-id}
volumes:
- qms-uploads:/app/uploads
depends_on:
mysql:
condition: service_healthy
networks:
- qms-net
volumes:
qms-mysql-data:
qms-uploads:
networks:
qms-net:
@@ -33,13 +33,14 @@ public class DefaultAdminService implements AdminService {
private final RoleService roleService;
private final PasswordEncoder passwordEncoder;
private final AuthService authService;
public DefaultAdminService(final UserRepository userRepository,
final UserService userService,
final TwoFactorService twoFactorService,
final TenantService tenantService,
final RoleService roleService,
final PasswordEncoder passwordEncoder,
final AuthService authService) {
final UserService userService,
final TwoFactorService twoFactorService,
final TenantService tenantService,
final RoleService roleService,
final PasswordEncoder passwordEncoder,
final AuthService authService) {
this.userRepository = userRepository;
this.userService = userService;
this.twoFactorService = twoFactorService;
@@ -50,7 +51,7 @@ public class DefaultAdminService implements AdminService {
}
@Override
public List<User> findUsersByCode(final String tenantCode, final String roleName){
public List<User> findUsersByCode(final String tenantCode, final String roleName) {
final Set<RoleName> roleNameSet = Set.of(RoleName.valueOf(roleName));
return this.userRepository.findAllByTenant_CodeAndRoles_NameIn(tenantCode, roleNameSet);
}
@@ -14,6 +14,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@@ -30,7 +31,8 @@ public class AdminController {
@PostMapping("/{code}")
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN', 'ROLE_STAFF_ADMIN')")
public ResponseEntity getUsers(@RequestBody RoleRequest request, @PathVariable(name = "code") final String tenantCode) {
public ResponseEntity<List<UserDto>> getUsers(@RequestBody RoleRequest request,
@PathVariable(name = "code") final String tenantCode) {
RoleName roleName;
try {
roleName = RoleName.valueOf(request.roleName);
@@ -39,7 +41,7 @@ public class AdminController {
return ResponseEntity.badRequest().build();
}
if(this.authService.canOnlyCRUDUser(roleName)){
if (this.authService.canOnlyCRUDUser(roleName)) {
logger.warn("Only super admin can read admins");
return ResponseEntity.badRequest().build();
}
@@ -52,8 +54,7 @@ public class AdminController {
return ResponseEntity.ok().body(
this.adminService.findUsersByCode(tenantCode, request.roleName).stream()
.map(UserDto::fromEntity)
.collect(Collectors.toList())
);
.collect(Collectors.toList()));
} catch (EntityNotFoundException e) {
return ResponseEntity.badRequest().build();
}
@@ -61,7 +62,8 @@ public class AdminController {
@PostMapping("/{code}/user")
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN', 'ROLE_STAFF_ADMIN')")
public ResponseEntity addUser(@RequestBody final AdminRequest request, @PathVariable(name = "code") final String tenantCode) throws AuthException {
public ResponseEntity<UserDto> addUser(@RequestBody final AdminRequest request,
@PathVariable(name = "code") final String tenantCode) throws AuthException {
RoleName roleName;
try {
roleName = RoleName.valueOf(request.roleName);
@@ -70,7 +72,7 @@ public class AdminController {
return ResponseEntity.badRequest().build();
}
if(this.authService.canOnlyCRUDUser(roleName)){
if (this.authService.canOnlyCRUDUser(roleName)) {
logger.warn("Only super admin can add admin");
return ResponseEntity.badRequest().build();
}
@@ -95,7 +97,8 @@ public class AdminController {
@PutMapping("/{code}/user/{userId}")
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN', 'ROLE_STAFF_ADMIN')")
public ResponseEntity updateAdmin(@RequestBody final UserDto request, @PathVariable(name = "code") final String tenantCode, @PathVariable(name = "userId") final long adminId) {
public ResponseEntity<SimpleMessageDto> updateAdmin(@RequestBody final UserDto request,
@PathVariable(name = "code") final String tenantCode, @PathVariable(name = "userId") final long adminId) {
if (!this.authService.canChangeTenant(tenantCode)) {
logger.warn("Admin does not belong to the specified tenant");
return ResponseEntity.badRequest().build();
@@ -112,7 +115,8 @@ public class AdminController {
@DeleteMapping("/{code}/user/{userId}")
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN', 'ROLE_STAFF_ADMIN')")
public ResponseEntity removeAdmin(@PathVariable(name = "code") final String tenantCode, @PathVariable(name = "userId") final long adminId) {
public ResponseEntity<SimpleMessageDto> removeAdmin(@PathVariable(name = "code") final String tenantCode,
@PathVariable(name = "userId") final long adminId) {
if (!this.authService.canChangeTenant(tenantCode)) {
logger.warn("Admin does not belong to the specified tenant");
return ResponseEntity.badRequest().build();
@@ -130,6 +134,6 @@ public class AdminController {
public record AdminRequest(String email, String password, String roleName) {
}
public record RoleRequest(String roleName){
public record RoleRequest(String roleName) {
}
}
@@ -30,13 +30,13 @@ public class AdvertisementController {
private final AuthService authService;
public AdvertisementController(final AdvertisementService advertisementService,
final AuthService authService) {
final AuthService authService) {
this.advertisementService = advertisementService;
this.authService = authService;
}
@GetMapping("/media/{adId}")
public ResponseEntity streamMedia(@PathVariable final long adId) {
public ResponseEntity<Resource> streamMedia(@PathVariable final long adId) {
try {
final Advertisement advertisement = this.advertisementService.findById(adId);
final Resource resource = this.advertisementService.loadMedia(adId);
@@ -64,11 +64,11 @@ public class AdvertisementController {
@PostMapping("/{tenantCode}")
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
public ResponseEntity createAdvertisement(@PathVariable final String tenantCode,
@RequestParam("file") final MultipartFile file,
@RequestParam(value = "title", required = false) final String title,
@RequestParam(value = "durationSeconds", required = false) final Integer durationSeconds,
@RequestParam(value = "sortOrder", required = false) final Integer sortOrder,
@RequestParam(value = "active", required = false) final Boolean active) {
@RequestParam("file") final MultipartFile file,
@RequestParam(value = "title", required = false) final String title,
@RequestParam(value = "durationSeconds", required = false) final Integer durationSeconds,
@RequestParam(value = "sortOrder", required = false) final Integer sortOrder,
@RequestParam(value = "active", required = false) final Boolean active) {
if (!this.authService.canChangeTenant(tenantCode)) {
return ResponseEntity.badRequest().build();
}
@@ -80,8 +80,7 @@ public class AdvertisementController {
title,
durationSeconds,
sortOrder,
active
);
active);
return ResponseEntity.ok().body(AdvertisementDto.fromEntity(created));
} catch (final Exception exception) {
return ResponseEntity.badRequest().body(new SimpleMessageDto(exception.getMessage()));
@@ -108,8 +107,8 @@ public class AdvertisementController {
@PutMapping("/{tenantCode}/{adId}")
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
public ResponseEntity updateAdvertisement(@PathVariable final String tenantCode,
@PathVariable final long adId,
@RequestBody final AdvertisementUpdateRequest request) {
@PathVariable final long adId,
@RequestBody final AdvertisementUpdateRequest request) {
if (!this.authService.canChangeTenant(tenantCode)) {
return ResponseEntity.badRequest().build();
}
@@ -121,8 +120,7 @@ public class AdvertisementController {
request.title(),
request.durationSeconds(),
request.sortOrder(),
request.active()
);
request.active());
return ResponseEntity.ok().body(AdvertisementDto.fromEntity(updated));
} catch (final Exception exception) {
return ResponseEntity.badRequest().body(new SimpleMessageDto(exception.getMessage()));
@@ -132,7 +130,7 @@ public class AdvertisementController {
@DeleteMapping("/{tenantCode}/{adId}")
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
public ResponseEntity deleteAdvertisement(@PathVariable final String tenantCode,
@PathVariable final long adId) {
@PathVariable final long adId) {
if (!this.authService.canChangeTenant(tenantCode)) {
return ResponseEntity.badRequest().build();
}
@@ -146,8 +144,8 @@ public class AdvertisementController {
}
public record AdvertisementUpdateRequest(String title,
Integer durationSeconds,
Integer sortOrder,
Boolean active) {
Integer durationSeconds,
Integer sortOrder,
Boolean active) {
}
}