diff --git a/backend/src/main/java/com/petshop/backend/controller/ChatAttachmentController.java b/backend/src/main/java/com/petshop/backend/controller/ChatAttachmentController.java deleted file mode 100644 index ba91d1f2..00000000 --- a/backend/src/main/java/com/petshop/backend/controller/ChatAttachmentController.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.petshop.backend.controller; - -import com.petshop.backend.service.ChatAttachmentService; -import org.springframework.core.io.Resource; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.util.Map; - -@RestController -@RequestMapping("/api/v1/chat/attachments") -public class ChatAttachmentController { - - private final ChatAttachmentService attachmentService; - - public ChatAttachmentController(ChatAttachmentService attachmentService) { - this.attachmentService = attachmentService; - } - - @PostMapping - @PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')") - public ResponseEntity uploadAttachment(@RequestParam("file") MultipartFile file) { - if (file.isEmpty()) { - return ResponseEntity.badRequest().body(Map.of("message", "File is empty")); - } - try { - return ResponseEntity.ok(attachmentService.storeAttachment(file)); - } catch (IOException e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(Map.of("message", "Failed to store attachment: " + e.getMessage())); - } - } - - @GetMapping("/{filename}") - @PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')") - public ResponseEntity getAttachment(@PathVariable String filename) { - try { - Resource resource = attachmentService.loadAttachmentResource(filename); - return ResponseEntity.ok() - .contentType(attachmentService.resolveMediaType(filename)) - .body(resource); - } catch (IllegalArgumentException e) { - return ResponseEntity.notFound().build(); - } - } -} diff --git a/backend/src/main/java/com/petshop/backend/dto/chat/MessageRequest.java b/backend/src/main/java/com/petshop/backend/dto/chat/MessageRequest.java index cecedfbd..2ffdeab4 100644 --- a/backend/src/main/java/com/petshop/backend/dto/chat/MessageRequest.java +++ b/backend/src/main/java/com/petshop/backend/dto/chat/MessageRequest.java @@ -2,10 +2,6 @@ package com.petshop.backend.dto.chat; public class MessageRequest { private String content; - private String attachmentUrl; - private String attachmentName; - private String attachmentMimeType; - private Long attachmentSizeBytes; public MessageRequest() { } @@ -17,36 +13,4 @@ public class MessageRequest { public void setContent(String content) { this.content = content; } - - public String getAttachmentUrl() { - return attachmentUrl; - } - - public void setAttachmentUrl(String attachmentUrl) { - this.attachmentUrl = attachmentUrl; - } - - public String getAttachmentName() { - return attachmentName; - } - - public void setAttachmentName(String attachmentName) { - this.attachmentName = attachmentName; - } - - public String getAttachmentMimeType() { - return attachmentMimeType; - } - - public void setAttachmentMimeType(String attachmentMimeType) { - this.attachmentMimeType = attachmentMimeType; - } - - public Long getAttachmentSizeBytes() { - return attachmentSizeBytes; - } - - public void setAttachmentSizeBytes(Long attachmentSizeBytes) { - this.attachmentSizeBytes = attachmentSizeBytes; - } } diff --git a/backend/src/main/java/com/petshop/backend/dto/chat/MessageResponse.java b/backend/src/main/java/com/petshop/backend/dto/chat/MessageResponse.java index ce0d413b..5bdaeb01 100644 --- a/backend/src/main/java/com/petshop/backend/dto/chat/MessageResponse.java +++ b/backend/src/main/java/com/petshop/backend/dto/chat/MessageResponse.java @@ -12,10 +12,6 @@ public class MessageResponse { private String content; private LocalDateTime timestamp; private Boolean isRead; - private String attachmentUrl; - private String attachmentName; - private String attachmentMimeType; - private Long attachmentSizeBytes; public MessageResponse() { } @@ -38,15 +34,6 @@ public class MessageResponse { response.setContent(message.getContent()); response.setTimestamp(message.getTimestamp()); response.setIsRead(message.getIsRead()); - - - if (message.getAttachmentUrl() != null) { - response.setAttachmentUrl("/api/v1/chat/messages/" + message.getId() + "/attachment"); - } - - response.setAttachmentName(message.getAttachmentName()); - response.setAttachmentMimeType(message.getAttachmentMimeType()); - response.setAttachmentSizeBytes(message.getAttachmentSizeBytes()); return response; } @@ -105,36 +92,4 @@ public class MessageResponse { public void setIsRead(Boolean isRead) { this.isRead = isRead; } - - public String getAttachmentUrl() { - return attachmentUrl; - } - - public void setAttachmentUrl(String attachmentUrl) { - this.attachmentUrl = attachmentUrl; - } - - public String getAttachmentName() { - return attachmentName; - } - - public void setAttachmentName(String attachmentName) { - this.attachmentName = attachmentName; - } - - public String getAttachmentMimeType() { - return attachmentMimeType; - } - - public void setAttachmentMimeType(String attachmentMimeType) { - this.attachmentMimeType = attachmentMimeType; - } - - public Long getAttachmentSizeBytes() { - return attachmentSizeBytes; - } - - public void setAttachmentSizeBytes(Long attachmentSizeBytes) { - this.attachmentSizeBytes = attachmentSizeBytes; - } } diff --git a/backend/src/main/java/com/petshop/backend/entity/Message.java b/backend/src/main/java/com/petshop/backend/entity/Message.java index 7c7bc498..ca0f15d4 100644 --- a/backend/src/main/java/com/petshop/backend/entity/Message.java +++ b/backend/src/main/java/com/petshop/backend/entity/Message.java @@ -22,17 +22,6 @@ public class Message { @Column(columnDefinition = "TEXT") private String content; - @Column(length = 255) - private String attachmentUrl; - - @Column(length = 255) - private String attachmentName; - - @Column(length = 100) - private String attachmentMimeType; - - private Long attachmentSizeBytes; - @CreationTimestamp @Column(nullable = false, updatable = false) private LocalDateTime timestamp; @@ -99,36 +88,4 @@ public class Message { public void setIsRead(Boolean isRead) { this.isRead = isRead; } - - public String getAttachmentUrl() { - return attachmentUrl; - } - - public void setAttachmentUrl(String attachmentUrl) { - this.attachmentUrl = attachmentUrl; - } - - public String getAttachmentName() { - return attachmentName; - } - - public void setAttachmentName(String attachmentName) { - this.attachmentName = attachmentName; - } - - public String getAttachmentMimeType() { - return attachmentMimeType; - } - - public void setAttachmentMimeType(String attachmentMimeType) { - this.attachmentMimeType = attachmentMimeType; - } - - public Long getAttachmentSizeBytes() { - return attachmentSizeBytes; - } - - public void setAttachmentSizeBytes(Long attachmentSizeBytes) { - this.attachmentSizeBytes = attachmentSizeBytes; - } } diff --git a/backend/src/main/java/com/petshop/backend/service/ChatAttachmentService.java b/backend/src/main/java/com/petshop/backend/service/ChatAttachmentService.java deleted file mode 100644 index a6ae312d..00000000 --- a/backend/src/main/java/com/petshop/backend/service/ChatAttachmentService.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.petshop.backend.service; - -import org.springframework.core.io.PathResource; -import org.springframework.core.io.Resource; -import org.springframework.http.MediaType; -import org.springframework.http.MediaTypeFactory; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.UUID; - -@Service -public class ChatAttachmentService { - - private static final String STORED_PREFIX = "/uploads/chat/"; - private final Path chatDirectory = Paths.get("uploads", "chat").toAbsolutePath().normalize(); - - public record AttachmentMetadata(String url, String fileName, String mimeType, long size) {} - - public AttachmentMetadata storeAttachment(MultipartFile file) throws IOException { - Files.createDirectories(chatDirectory); - - String originalFilename = file.getOriginalFilename(); - String extension = resolveExtension(originalFilename); - String filename = UUID.randomUUID() + extension; - Path filePath = chatDirectory.resolve(filename).normalize(); - - Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING); - - String url = "/api/v1/chat/attachments/" + filename; - return new AttachmentMetadata(url, originalFilename, file.getContentType(), file.getSize()); - } - - public Resource loadAttachmentResource(String filename) { - Path filePath = chatDirectory.resolve(filename).normalize(); - if (!filePath.startsWith(chatDirectory) || !Files.exists(filePath) || !Files.isRegularFile(filePath)) { - throw new IllegalArgumentException("Attachment file was not found"); - } - return new PathResource(filePath); - } - - public MediaType resolveMediaType(String filename) { - return MediaTypeFactory.getMediaType(filename).orElse(MediaType.APPLICATION_OCTET_STREAM); - } - - private String resolveExtension(String originalFilename) { - if (originalFilename == null) return ""; - int idx = originalFilename.lastIndexOf('.'); - return idx < 0 ? "" : originalFilename.substring(idx); - } -} diff --git a/backend/src/main/java/com/petshop/backend/service/ChatService.java b/backend/src/main/java/com/petshop/backend/service/ChatService.java index 15f18a47..723765e4 100644 --- a/backend/src/main/java/com/petshop/backend/service/ChatService.java +++ b/backend/src/main/java/com/petshop/backend/service/ChatService.java @@ -131,10 +131,6 @@ public class ChatService { message.setConversationId(conversationId); message.setSenderId(userId); message.setContent(request.getContent()); - message.setAttachmentUrl(request.getAttachmentUrl()); - message.setAttachmentName(request.getAttachmentName()); - message.setAttachmentMimeType(request.getAttachmentMimeType()); - message.setAttachmentSizeBytes(request.getAttachmentSizeBytes()); message.setIsRead(false); message = messageRepository.save(message); diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/ChatAttachmentResponse.java b/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/ChatAttachmentResponse.java deleted file mode 100644 index d8b70c11..00000000 --- a/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/ChatAttachmentResponse.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.example.petshopdesktop.api.dto.chat; - -public class ChatAttachmentResponse { - private String url; - private String fileName; - private String mimeType; - private long size; - - public ChatAttachmentResponse() { - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } - - public String getMimeType() { - return mimeType; - } - - public void setMimeType(String mimeType) { - this.mimeType = mimeType; - } - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } -} diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/MessageRequest.java b/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/MessageRequest.java index cd8efedf..a5c17ca4 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/MessageRequest.java +++ b/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/MessageRequest.java @@ -2,10 +2,6 @@ package org.example.petshopdesktop.api.dto.chat; public class MessageRequest { private String content; - private String attachmentUrl; - private String attachmentName; - private String attachmentMimeType; - private Long attachmentSizeBytes; public MessageRequest() { } @@ -21,36 +17,4 @@ public class MessageRequest { public void setContent(String content) { this.content = content; } - - public String getAttachmentUrl() { - return attachmentUrl; - } - - public void setAttachmentUrl(String attachmentUrl) { - this.attachmentUrl = attachmentUrl; - } - - public String getAttachmentName() { - return attachmentName; - } - - public void setAttachmentName(String attachmentName) { - this.attachmentName = attachmentName; - } - - public String getAttachmentMimeType() { - return attachmentMimeType; - } - - public void setAttachmentMimeType(String attachmentMimeType) { - this.attachmentMimeType = attachmentMimeType; - } - - public Long getAttachmentSizeBytes() { - return attachmentSizeBytes; - } - - public void setAttachmentSizeBytes(Long attachmentSizeBytes) { - this.attachmentSizeBytes = attachmentSizeBytes; - } } diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/MessageResponse.java b/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/MessageResponse.java index 84e64605..6d5c6407 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/MessageResponse.java +++ b/desktop/src/main/java/org/example/petshopdesktop/api/dto/chat/MessageResponse.java @@ -10,10 +10,6 @@ public class MessageResponse { private String content; private LocalDateTime timestamp; private Boolean isRead; - private String attachmentUrl; - private String attachmentName; - private String attachmentMimeType; - private Long attachmentSizeBytes; public MessageResponse() { } @@ -73,36 +69,4 @@ public class MessageResponse { public void setIsRead(Boolean isRead) { this.isRead = isRead; } - - public String getAttachmentUrl() { - return attachmentUrl; - } - - public void setAttachmentUrl(String attachmentUrl) { - this.attachmentUrl = attachmentUrl; - } - - public String getAttachmentName() { - return attachmentName; - } - - public void setAttachmentName(String attachmentName) { - this.attachmentName = attachmentName; - } - - public String getAttachmentMimeType() { - return attachmentMimeType; - } - - public void setAttachmentMimeType(String attachmentMimeType) { - this.attachmentMimeType = attachmentMimeType; - } - - public Long getAttachmentSizeBytes() { - return attachmentSizeBytes; - } - - public void setAttachmentSizeBytes(Long attachmentSizeBytes) { - this.attachmentSizeBytes = attachmentSizeBytes; - } } diff --git a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/ChatApi.java b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/ChatApi.java index b9898820..3fcb7372 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/ChatApi.java +++ b/desktop/src/main/java/org/example/petshopdesktop/api/endpoints/ChatApi.java @@ -2,13 +2,10 @@ package org.example.petshopdesktop.api.endpoints; import com.fasterxml.jackson.core.type.TypeReference; import org.example.petshopdesktop.api.ApiClient; -import org.example.petshopdesktop.api.dto.chat.ChatAttachmentResponse; import org.example.petshopdesktop.api.dto.chat.ConversationRequest; import org.example.petshopdesktop.api.dto.chat.ConversationResponse; import org.example.petshopdesktop.api.dto.chat.MessageRequest; import org.example.petshopdesktop.api.dto.chat.MessageResponse; - -import java.nio.file.Path; import java.util.List; public class ChatApi { @@ -44,8 +41,4 @@ public class ChatApi { public MessageResponse sendMessage(Long conversationId, MessageRequest request) throws Exception { return apiClient.post("/api/v1/chat/conversations/" + conversationId + "/messages", request, MessageResponse.class); } - - public ChatAttachmentResponse uploadAttachment(Path filePath) throws Exception { - return apiClient.postMultipart("/api/v1/chat/attachments", "file", filePath, ChatAttachmentResponse.class); - } } diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java index 48a90ed8..02d05a93 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java @@ -20,7 +20,6 @@ import javafx.scene.paint.ImagePattern; import javafx.scene.shape.Circle; import javafx.scene.image.Image; import org.example.petshopdesktop.api.ChatRealtimeClient; -import org.example.petshopdesktop.api.dto.chat.ChatAttachmentResponse; import org.example.petshopdesktop.api.dto.chat.ConversationResponse; import org.example.petshopdesktop.api.dto.chat.MessageRequest; import org.example.petshopdesktop.api.dto.chat.MessageResponse; @@ -30,6 +29,7 @@ import org.example.petshopdesktop.api.endpoints.DropdownApi; import org.example.petshopdesktop.auth.UserSession; import org.example.petshopdesktop.util.ActivityLogger; +import java.io.File; import java.time.format.DateTimeFormatter; import java.util.Comparator; import java.util.HashMap; @@ -71,7 +71,7 @@ public class ChatController { private final Map customerLabels = new HashMap<>(); private final ChatRealtimeClient realtimeClient = ChatRealtimeClient.getInstance(); private ConversationResponse selectedConversation; - private ChatAttachmentResponse selectedAttachment; + private File selectedAttachmentFile; @FXML public void initialize() { @@ -155,42 +155,38 @@ public class ChatController { } String content = txtMessage.getText() == null ? "" : txtMessage.getText().trim(); - if (content.isEmpty() && selectedAttachment == null) { + if (content.isEmpty()) { + if (selectedAttachmentFile != null) { + lblChatStatus.setText("Attachments are not available yet"); + } return; } Long convId = selectedConversation.getId(); - var attachment = selectedAttachment; txtMessage.clear(); btnSend.setDisable(true); - selectedAttachment = null; - btnAttachment.setText("📎"); - btnAttachment.setStyle("-fx-background-color: #e2e8f0; -fx-background-radius: 12; -fx-text-fill: #475569; -fx-cursor: hand;"); lblChatStatus.setText("Sending message..."); new Thread(() -> { try { MessageRequest request = new MessageRequest(content); - if (attachment != null) { - request.setAttachmentUrl(attachment.getUrl()); - request.setAttachmentName(attachment.getFileName()); - request.setAttachmentMimeType(attachment.getMimeType()); - request.setAttachmentSizeBytes(attachment.getSize()); - } MessageResponse response = ChatApi.getInstance().sendMessage(convId, request); Platform.runLater(() -> { btnSend.setDisable(false); appendMessageIfSelected(response); - lblChatStatus.setText("Message sent"); + if (selectedAttachmentFile != null) { + clearLocalAttachment(); + lblChatStatus.setText("Message sent without attachment"); + } else { + lblChatStatus.setText("Message sent"); + } }); } catch (Exception e) { Platform.runLater(() -> { txtMessage.setText(content); btnSend.setDisable(false); - selectedAttachment = attachment; - btnAttachment.setText("📎 " + attachment.getFileName()); lblChatStatus.setText("Chat send failed"); ActivityLogger.getInstance().logException( "ChatController.sendMessage", @@ -203,30 +199,19 @@ public class ChatController { @FXML void btnAttachmentClicked(ActionEvent event) { - java.io.File file = org.example.petshopdesktop.util.FilePickerSupport.pickAnyFile(btnAttachment.getScene().getWindow()); + File file = org.example.petshopdesktop.util.FilePickerSupport.pickAnyFile(btnAttachment.getScene().getWindow()); if (file == null) return; - btnAttachment.setDisable(true); - lblChatStatus.setText("Uploading attachment..."); + selectedAttachmentFile = file; + btnAttachment.setText("📎 " + file.getName()); + btnAttachment.setStyle("-fx-background-color: #dcfce7; -fx-background-radius: 12; -fx-text-fill: #166534; -fx-cursor: hand;"); + lblChatStatus.setText("Attachment selected"); + } - new Thread(() -> { - try { - var response = ChatApi.getInstance().uploadAttachment(file.toPath()); - Platform.runLater(() -> { - selectedAttachment = response; - btnAttachment.setText("📎 " + response.getFileName()); - btnAttachment.setStyle("-fx-background-color: #dcfce7; -fx-background-radius: 12; -fx-text-fill: #166534; -fx-cursor: hand;"); - lblChatStatus.setText("File ready to send"); - btnAttachment.setDisable(false); - }); - } catch (Exception e) { - ActivityLogger.getInstance().logException("ChatController.btnAttachmentClicked", e, "Uploading chat attachment"); - Platform.runLater(() -> { - lblChatStatus.setText("Upload failed"); - btnAttachment.setDisable(false); - }); - } - }).start(); + private void clearLocalAttachment() { + selectedAttachmentFile = null; + btnAttachment.setText("📎"); + btnAttachment.setStyle("-fx-background-color: #e2e8f0; -fx-background-radius: 12; -fx-text-fill: #475569; -fx-cursor: hand;"); } private void loadCustomers() { @@ -397,26 +382,6 @@ public class ChatController { bubble.getChildren().add(content); } - if (message.getAttachmentUrl() != null && !message.getAttachmentUrl().isBlank()) { - String attachmentLabel = "📎 " + (message.getAttachmentName() == null || message.getAttachmentName().isBlank() ? "Attachment" : message.getAttachmentName()); - if (message.getAttachmentSizeBytes() != null && message.getAttachmentSizeBytes() > 0) { - attachmentLabel = attachmentLabel + " (" + formatSize(message.getAttachmentSizeBytes()) + ")"; - } - Button attachmentBtn = new Button(attachmentLabel); - attachmentBtn.setStyle("-fx-background-color: " + (mine ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.05)") + "; -fx-text-fill: " + (mine ? "#ffffff" : "#0f766e") + "; -fx-cursor: hand; -fx-background-radius: 8; -fx-padding: 6 10;"); - attachmentBtn.setOnAction(e -> { - try { - String fullUrl = org.example.petshopdesktop.api.ApiConfig.getInstance().getBaseUrl() + message.getAttachmentUrl(); - if (java.awt.Desktop.isDesktopSupported()) { - java.awt.Desktop.getDesktop().browse(new java.net.URI(fullUrl)); - } - } catch (Exception ex) { - ActivityLogger.getInstance().logException("ChatController.attachmentOpen", ex, "Opening attachment URL"); - } - }); - bubble.getChildren().add(attachmentBtn); - } - bubble.getChildren().add(timestamp); bubble.setMaxWidth(420); bubble.setStyle(mine @@ -475,17 +440,4 @@ public class ChatController { private void scrollMessagesToBottom() { Platform.runLater(() -> spMessages.setVvalue(1.0)); } - private String formatSize(Long bytes) { - if (bytes == null || bytes <= 0) { - return ""; - } - double size = bytes; - String[] units = {"B", "KB", "MB", "GB"}; - int unitIndex = 0; - while (size >= 1024 && unitIndex < units.length - 1) { - size = size / 1024; - unitIndex++; - } - return unitIndex == 0 ? String.format("%.0f %s", size, units[unitIndex]) : String.format("%.1f %s", size, units[unitIndex]); - } }