fix validation bugs
This commit is contained in:
@@ -309,17 +309,18 @@ public class AuthController {
|
|||||||
public ResponseEntity<Resource> getAvatarFile() {
|
public ResponseEntity<Resource> getAvatarFile() {
|
||||||
User user = authHelper.getAuthenticatedUser();
|
User user = authHelper.getAuthenticatedUser();
|
||||||
|
|
||||||
if (!avatarStorageService.hasAvatar(user)) {
|
if (avatarStorageService.hasAvatar(user)) {
|
||||||
return ResponseEntity.notFound().build();
|
try {
|
||||||
|
Resource resource = avatarStorageService.loadAvatarResource(user);
|
||||||
|
MediaType mediaType = avatarStorageService.resolveMediaType(user);
|
||||||
|
return ResponseEntity.ok().contentType(mediaType).body(resource);
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return ResponseEntity.ok()
|
||||||
Resource resource = avatarStorageService.loadAvatarResource(user);
|
.contentType(MediaType.valueOf("image/svg+xml"))
|
||||||
MediaType mediaType = avatarStorageService.resolveMediaType(user);
|
.body(avatarStorageService.loadDefaultAvatarResource());
|
||||||
return ResponseEntity.ok().contentType(mediaType).body(resource);
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
return ResponseEntity.notFound().build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/me/avatar")
|
@DeleteMapping("/me/avatar")
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.petshop.backend.repository.UserRepository;
|
|||||||
import com.petshop.backend.service.AvatarStorageService;
|
import com.petshop.backend.service.AvatarStorageService;
|
||||||
import com.petshop.backend.util.ImageValidationUtil;
|
import com.petshop.backend.util.ImageValidationUtil;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
@@ -42,18 +43,23 @@ public class UserAvatarController {
|
|||||||
@PreAuthorize("isAuthenticated()")
|
@PreAuthorize("isAuthenticated()")
|
||||||
public ResponseEntity<Resource> getUserAvatarFile(@PathVariable Long userId) {
|
public ResponseEntity<Resource> getUserAvatarFile(@PathVariable Long userId) {
|
||||||
User user = userRepository.findById(userId).orElse(null);
|
User user = userRepository.findById(userId).orElse(null);
|
||||||
if (user == null || !avatarStorageService.hasAvatar(user)) {
|
if (user == null) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (avatarStorageService.hasAvatar(user)) {
|
||||||
Resource resource = avatarStorageService.loadAvatarResource(user);
|
try {
|
||||||
return ResponseEntity.ok()
|
Resource resource = avatarStorageService.loadAvatarResource(user);
|
||||||
.contentType(avatarStorageService.resolveMediaType(user))
|
return ResponseEntity.ok()
|
||||||
.body(resource);
|
.contentType(avatarStorageService.resolveMediaType(user))
|
||||||
} catch (IllegalArgumentException ex) {
|
.body(resource);
|
||||||
return ResponseEntity.notFound().build();
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.contentType(MediaType.valueOf("image/svg+xml"))
|
||||||
|
.body(avatarStorageService.loadDefaultAvatarResource());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{userId}/avatar")
|
@PostMapping("/{userId}/avatar")
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ public class AppointmentService {
|
|||||||
appointment.setEmployee(employee);
|
appointment.setEmployee(employee);
|
||||||
appointment.setAppointmentDate(request.getAppointmentDate());
|
appointment.setAppointmentDate(request.getAppointmentDate());
|
||||||
appointment.setAppointmentTime(request.getAppointmentTime());
|
appointment.setAppointmentTime(request.getAppointmentTime());
|
||||||
appointment.setAppointmentStatus(request.getAppointmentStatus());
|
appointment.setAppointmentStatus("Scheduled");
|
||||||
appointment.setPet(pet);
|
appointment.setPet(pet);
|
||||||
|
|
||||||
appointment = appointmentRepository.save(appointment);
|
appointment = appointmentRepository.save(appointment);
|
||||||
@@ -304,11 +304,9 @@ public class AppointmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void validateAppointmentRequest(AppointmentRequest request) {
|
private void validateAppointmentRequest(AppointmentRequest request) {
|
||||||
if ("Booked".equalsIgnoreCase(request.getAppointmentStatus())) {
|
LocalDateTime appointmentDateTime = LocalDateTime.of(request.getAppointmentDate(), request.getAppointmentTime());
|
||||||
LocalDateTime appointmentDateTime = LocalDateTime.of(request.getAppointmentDate(), request.getAppointmentTime());
|
if (appointmentDateTime.isBefore(LocalDateTime.now())) {
|
||||||
if (appointmentDateTime.isBefore(LocalDateTime.now())) {
|
throw new BusinessException("Appointments must be scheduled in the future");
|
||||||
throw new BusinessException("Booked appointments must be scheduled in the future");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -54,7 +55,11 @@ public class AvatarStorageService {
|
|||||||
public Resource loadAvatarResource(User user) {
|
public Resource loadAvatarResource(User user) {
|
||||||
String filename = extractFilename(user.getAvatarUrl());
|
String filename = extractFilename(user.getAvatarUrl());
|
||||||
if (blobService.isEnabled()) {
|
if (blobService.isEnabled()) {
|
||||||
return new ByteArrayResource(blobService.download(BLOB_CONTAINER, filename));
|
try {
|
||||||
|
return new ByteArrayResource(blobService.download(BLOB_CONTAINER, filename));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalArgumentException("Avatar file was not found");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Path filePath = resolveStoredAvatarPath(user.getAvatarUrl());
|
Path filePath = resolveStoredAvatarPath(user.getAvatarUrl());
|
||||||
if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) {
|
if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) {
|
||||||
@@ -63,6 +68,18 @@ public class AvatarStorageService {
|
|||||||
return new PathResource(filePath);
|
return new PathResource(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Resource loadDefaultAvatarResource() {
|
||||||
|
InputStream is = getClass().getResourceAsStream("/static/default-avatar.svg");
|
||||||
|
if (is == null) {
|
||||||
|
throw new IllegalStateException("Default avatar resource not found");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new ByteArrayResource(is.readAllBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("Failed to read default avatar", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteAvatar(User user) throws IOException {
|
public void deleteAvatar(User user) throws IOException {
|
||||||
if (user.getAvatarUrl() == null || user.getAvatarUrl().isBlank()) return;
|
if (user.getAvatarUrl() == null || user.getAvatarUrl().isBlank()) return;
|
||||||
if (blobService.isEnabled()) {
|
if (blobService.isEnabled()) {
|
||||||
@@ -80,7 +97,7 @@ public class AvatarStorageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String toOwnerAvatarUrl(User user) {
|
public String toOwnerAvatarUrl(User user) {
|
||||||
return hasAvatar(user) ? "/api/v1/users/" + user.getId() + "/avatar/file" : null;
|
return "/api/v1/users/" + user.getId() + "/avatar/file";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toStoredAvatarUrl(String avatarFilenamePath) {
|
public String toStoredAvatarUrl(String avatarFilenamePath) {
|
||||||
|
|||||||
@@ -146,6 +146,12 @@ public class ChatService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasContent = request.getContent() != null && !request.getContent().isBlank();
|
||||||
|
boolean hasAttachment = request.getAttachmentUrl() != null && !request.getAttachmentUrl().isBlank();
|
||||||
|
if (!hasContent && !hasAttachment) {
|
||||||
|
throw new BusinessException("Message must have content or an attachment");
|
||||||
|
}
|
||||||
|
|
||||||
ContentFilter.validate(request.getContent());
|
ContentFilter.validate(request.getContent());
|
||||||
|
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
|
|||||||
@@ -89,6 +89,10 @@ public class CouponService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateCouponFields(Coupon coupon, CouponRequest request) {
|
private void updateCouponFields(Coupon coupon, CouponRequest request) {
|
||||||
|
if ("PERCENTAGE".equalsIgnoreCase(request.getDiscountType())
|
||||||
|
&& request.getDiscountValue().compareTo(new BigDecimal("100")) >= 0) {
|
||||||
|
throw new BusinessException("Percentage discount must be less than 100");
|
||||||
|
}
|
||||||
coupon.setCouponCode(request.getCouponCode());
|
coupon.setCouponCode(request.getCouponCode());
|
||||||
coupon.setDiscountType(request.getDiscountType());
|
coupon.setDiscountType(request.getDiscountType());
|
||||||
coupon.setDiscountValue(request.getDiscountValue());
|
coupon.setDiscountValue(request.getDiscountValue());
|
||||||
|
|||||||
1
backend/src/main/resources/static/default-avatar.svg
Normal file
1
backend/src/main/resources/static/default-avatar.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="128" height="128"><circle cx="64" cy="64" r="64" fill="#9e9e9e"/><circle cx="64" cy="50" r="22" fill="#fff"/><ellipse cx="64" cy="114" rx="40" ry="36" fill="#fff"/></svg>
|
||||||
|
After Width: | Height: | Size: 240 B |
Reference in New Issue
Block a user