Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.example.team6backend.activity.controller;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.team6backend.activity.dto.ActivityLogResponse;
import org.example.team6backend.activity.service.ActivityLogService;
import org.springframework.http.ResponseEntity;
Expand All @@ -12,19 +14,17 @@

@RestController
@RequestMapping("/activity")
@RequiredArgsConstructor
@Slf4j
public class ActivityLogController {

private final ActivityLogService activityLogService;

public ActivityLogController(ActivityLogService activityLogService) {
this.activityLogService = activityLogService;
}

@GetMapping("/incident/{incidentId}")
public ResponseEntity<List<ActivityLogResponse>> getActivityByIncidentId(@PathVariable Long incidentId) {
log.info("GET /activity/incident/{} - Fetching activity log", incidentId);
List<ActivityLogResponse> activityLogs = activityLogService.getByIncidentId(incidentId).stream()
.map(ActivityLogResponse::fromEntity).toList();

return ResponseEntity.ok(activityLogs);
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
package org.example.team6backend.comment.controller;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.team6backend.comment.dto.CommentRequest;
import org.example.team6backend.comment.dto.CommentResponse;
import org.example.team6backend.comment.service.CommentService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/comments")
@RequiredArgsConstructor
@Slf4j
public class CommentController {

private final CommentService commentService;

public CommentController(CommentService commentService) {
this.commentService = commentService;
}

@GetMapping("/incident/{incidentId}")
public ResponseEntity<List<CommentResponse>> getCommentByIncidentId(@PathVariable Long incidentId) {
log.info("GET /comments/incident/{} - Fetching comments", incidentId);
List<CommentResponse> comments = commentService.getCommentByIncidentId(incidentId).stream()
.map(CommentResponse::fromEntity).toList();

return ResponseEntity.ok(comments);
}

@PostMapping
public ResponseEntity<Void> createComment(@RequestBody @Valid CommentRequest request) {
commentService.createComment(request.getIncidentId(), request.getUserId(), request.getMessage());
return ResponseEntity.ok().build();
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<CommentResponse> createComment(@Valid @RequestBody CommentRequest request) {
log.info("POST /comments - Creating comment for incident: {}", request.getIncidentId());
var comment = commentService.createComment(request.getIncidentId(), request.getUserId(), request.getMessage());
return ResponseEntity.status(HttpStatus.CREATED).body(CommentResponse.fromEntity(comment));
}
}
21 changes: 13 additions & 8 deletions src/main/java/org/example/team6backend/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
Expand All @@ -18,19 +19,23 @@ public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(
auth -> auth.requestMatchers("/", "/index", "/demo", "/error", "/login/**", "/oauth2/**", "/dev/**")
.permitAll().requestMatchers("/dashboard.html", "/profile.html", "/viewincident.html")
.authenticated().requestMatchers("/incidents.html/**", "/api/incidents/**")
.hasAnyRole("RESIDENT", "HANDLER", "ADMIN").requestMatchers("/admin.html", "/api/admin/**")
.hasRole("ADMIN").requestMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**")
.hasRole("ADMIN").anyRequest().authenticated())
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/index.html", "/dashboard.html", "/incidents.html", "/profile.html",
"/createincident.html", "/viewincident.html", "/admin.html", "/demo.html", "/error",
"/login/**", "/oauth2/**", "/dev/**", "/css/**", "/js/**", "/images/**")
.permitAll()

.requestMatchers("/api/incidents/**").hasAnyRole("RESIDENT", "HANDLER", "ADMIN")
.requestMatchers("/api/admin/**").hasRole("ADMIN").requestMatchers("/api/users/me").authenticated()
.requestMatchers("/comments/**", "/activity/**", "/documents/**").authenticated()
.requestMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**").hasRole("ADMIN").anyRequest()
.authenticated())
.oauth2Login(
oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService))
.defaultSuccessUrl("/dashboard.html", true))
.logout(logout -> logout.logoutSuccessUrl("/").invalidateHttpSession(true).clearAuthentication(true)
.deleteCookies("JSESSIONID"))
.csrf(csrf -> csrf.ignoringRequestMatchers("/api/admin/**", "/api/incidents/**", "/api/documents/**",
.csrf(csrf -> csrf.ignoringRequestMatchers("/api/admin/**", "/api/incidents/**", "/comments",
"/dev/**"));

return http.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.example.team6backend.document.controller;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.team6backend.document.dto.DocumentDTO;
import org.example.team6backend.document.entity.Document;
import org.example.team6backend.document.service.DocumentService;
import org.example.team6backend.document.service.MinioService;
Expand All @@ -19,31 +22,24 @@
import org.springframework.web.server.ResponseStatusException;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/api/documents")
@RequestMapping("/documents")
@RequiredArgsConstructor
@Slf4j
public class DocumentController {

private final DocumentService documentService;
private final IncidentService incidentService;
private final MinioService minioService;

public DocumentController(DocumentService documentService, IncidentService incidentService,
MinioService minioService) {
this.documentService = documentService;
this.incidentService = incidentService;
this.minioService = minioService;
}

@GetMapping("/{fileKey}")
public ResponseEntity<Resource> getFile(@PathVariable String fileKey,
@AuthenticationPrincipal CustomUserDetails userDetails) {

if (userDetails == null) {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}

log.info("GET /documents/{} - Fetching file", fileKey);
AppUser user = userDetails.getUser();

Document document = documentService.getByFileKey(fileKey)
Expand All @@ -55,7 +51,6 @@ public ResponseEntity<Resource> getFile(@PathVariable String fileKey,
}

InputStream inputStream = minioService.getFile(fileKey);

MediaType mediaType = document.getContentType() != null
? MediaType.parseMediaType(document.getContentType())
: MediaType.APPLICATION_OCTET_STREAM;
Expand All @@ -66,33 +61,42 @@ public ResponseEntity<Resource> getFile(@PathVariable String fileKey,
}

@PostMapping("/upload/{incidentId}")
public ResponseEntity<Void> uploadFile(@PathVariable Long incidentId,
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<List<DocumentDTO>> uploadFile(@PathVariable Long incidentId,
@RequestParam("files") List<MultipartFile> files, @AuthenticationPrincipal CustomUserDetails userDetails) {

log.info("POST /documents/upload/{} - Uploading {} files", incidentId, files.size());
AppUser user = userDetails.getUser();
Incident incident = incidentService.getById(incidentId, user);
List<DocumentDTO> uploadedDocs = new ArrayList<>();

for (MultipartFile file : files) {
if (!file.isEmpty()) {
documentService.uploadFile(file, incident);
Document doc = documentService.uploadFile(file, incident);
DocumentDTO dto = new DocumentDTO();
dto.setFileName(doc.getFileName());
dto.setFileKey(doc.getFileKey());
dto.setContentType(doc.getContentType());
dto.setFileSize(doc.getFileSize());
dto.setImage(doc.getContentType() != null && doc.getContentType().startsWith("image/"));
uploadedDocs.add(dto);
log.debug("Uploaded file: {}", doc.getFileName());
}
}
return ResponseEntity.ok().build();

return ResponseEntity.status(HttpStatus.CREATED).body(uploadedDocs);
}

@GetMapping("/{fileKey}/download")
public ResponseEntity<InputStreamResource> downloadFile(@PathVariable String fileKey,
@DeleteMapping("/{documentId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public ResponseEntity<Void> deleteFile(@PathVariable Long documentId,
@AuthenticationPrincipal CustomUserDetails userDetails) {
AppUser user = userDetails.getUser();

Document document = documentService.getByFileKey(fileKey)
log.info("DELETE /documents/{} - Deleting file", documentId);
Document document = documentService.getById(documentId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));

InputStream stream = minioService.getFile(fileKey);

return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + document.getFileName() + "\"")
.contentType(MediaType.parseMediaType(document.getContentType())).body(new InputStreamResource(stream));

documentService.deleteFile(document);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
public class DocumentDTO {
private String fileName;
private String fileKey;
private String fileUrl;
private String contentType;
private Long fileSize;
private boolean image;
private String fileUrl;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
import java.util.Optional;

public interface DocumentRepository extends JpaRepository<Document, Long> {

List<Document> findByIncident(Incident incident);

List<Document> findByIncidentId(Long incidentId);

Optional<Document> findByFileKey(String fileKey);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.List;
import java.util.Optional;
Expand All @@ -22,8 +23,8 @@ public class DocumentService {
private final DocumentRepository documentRepository;

/** Upload file */
@Transactional
public Document uploadFile(MultipartFile file, Incident incident) {

String fileKey = UUID.randomUUID() + "_" + file.getOriginalFilename();
boolean uploaded = false;

Expand All @@ -45,7 +46,7 @@ public Document uploadFile(MultipartFile file, Incident incident) {
try {
minioService.deleteFile(fileKey);
} catch (Exception cleanupEx) {
log.warn("Failed to cleanup S3 file: {}", fileKey, cleanupEx);
log.warn("Failed to cleanup Minio file: {}", fileKey, cleanupEx);
}
}
throw new RuntimeException("File upload failed", e);
Expand All @@ -60,19 +61,32 @@ public InputStream downloadFile(String objectKey) {
/** Delete file */
@Transactional
public void deleteFile(Document document) {
documentRepository.delete(document);

try {
documentRepository.delete(document);
minioService.deleteFile(document.getFileKey());
} catch (RuntimeException e) {
} catch (Exception e) {
log.warn("Could not delete file: {}", document.getFileKey(), e);
}
}

/** Fetch all files connected to one incident */
public List<Document> getDocumentsByIncident(Incident incidentId) {
return documentRepository.findByIncident(incidentId);
public List<Document> getDocumentsByIncident(Incident incident) {
return documentRepository.findByIncident(incident);
}

/** Fetch document by fileKey */
public Optional<Document> getByFileKey(String fileKey) {
return documentRepository.findByFileKey(fileKey);
}

/** Fetch document by ID */
public Optional<Document> getById(Long documentId) {
return documentRepository.findById(documentId);
}

/** Fetch documents by incident ID */
public List<Document> getDocumentsByIncidentId(Long incidentId) {
return documentRepository.findByIncidentId(incidentId);
}
}
Loading
Loading