fix: FilePreview fileType case + Tailwind v4 gradient transparent bug
- FilePreview.vue: add normalizedFileType computed to handle backend returning uppercase HTML/MD/PPTX (fixes preview/download buttons) - FilePreview.vue: bg-gradient-to-r from-orange-500 -> bg-orange-500 (Tailwind v4 gradient + CSS variable = transparent) - ReportCard.vue: bg-gradient-to-r -> bg-orange-600 for selected state - Add .opencode/, node_modules/, dist/ to .gitignore - Initial git setup for publish project
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
package com.reportdist;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class DailyReportDistributionApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DailyReportDistributionApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.reportdist.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/api/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/uploads/**")
|
||||
.addResourceLocations("file:./uploads/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.reportdist.controller;
|
||||
|
||||
import com.reportdist.dto.ProjectRequest;
|
||||
import com.reportdist.dto.ProjectResponse;
|
||||
import com.reportdist.service.ProjectService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/projects")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class ProjectController {
|
||||
|
||||
private final ProjectService projectService;
|
||||
|
||||
public ProjectController(ProjectService projectService) {
|
||||
this.projectService = projectService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<List<ProjectResponse>> getAllProjects() {
|
||||
return ResponseEntity.ok(projectService.getAllProjects());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<ProjectResponse> getProjectById(@PathVariable Long id) {
|
||||
return ResponseEntity.ok(projectService.getProjectById(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<ProjectResponse> createProject(@Valid @RequestBody ProjectRequest request) {
|
||||
ProjectResponse response = projectService.createProject(request);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(response);
|
||||
}
|
||||
|
||||
@PutMapping(value = "/{id}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public ResponseEntity<ProjectResponse> updateProject(
|
||||
@PathVariable Long id,
|
||||
@RequestParam(value = "name", required = false) String name,
|
||||
@RequestParam(value = "description", required = false) String description,
|
||||
@RequestParam(value = "coverImage", required = false) MultipartFile coverImage) {
|
||||
ProjectResponse response = projectService.updateProject(id, name, description, coverImage);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> deleteProject(@PathVariable Long id) {
|
||||
projectService.deleteProject(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.reportdist.controller;
|
||||
|
||||
import com.reportdist.dto.ReportRequest;
|
||||
import com.reportdist.dto.ReportResponse;
|
||||
import com.reportdist.service.ReportService;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/reports")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class ReportController {
|
||||
|
||||
private final ReportService reportService;
|
||||
|
||||
public ReportController(ReportService reportService) {
|
||||
this.reportService = reportService;
|
||||
}
|
||||
|
||||
@GetMapping("/ping")
|
||||
public ResponseEntity<?> ping() {
|
||||
return ResponseEntity.ok(java.util.Map.of("version", "v4-diag", "timestamp", java.time.Instant.now().toString()));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<?> getAllReports(@RequestParam(required = false) Long projectId) {
|
||||
return ResponseEntity.ok(reportService.getAllReports(projectId));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<ReportResponse> getReportById(@PathVariable Long id) {
|
||||
return ResponseEntity.ok(reportService.getReportById(id));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/preview")
|
||||
public ResponseEntity<byte[]> previewReport(@PathVariable Long id) {
|
||||
ReportResponse report = reportService.getReportById(id);
|
||||
String contentType = "application/octet-stream";
|
||||
switch (report.getFileType().toLowerCase()) {
|
||||
case "html": contentType = "text/html; charset=utf-8"; break;
|
||||
case "md": contentType = "text/markdown; charset=utf-8"; break;
|
||||
case "pdf": contentType = "application/pdf"; break;
|
||||
case "pptx": contentType = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; break;
|
||||
case "ppt": contentType = "application/vnd.ms-powerpoint"; break;
|
||||
}
|
||||
byte[] bytes = reportService.getReportBytes(id);
|
||||
return ResponseEntity.ok()
|
||||
.header("Content-Type", contentType)
|
||||
.header("Content-Disposition", "inline; filename=\"" + report.getFileName() + "\"")
|
||||
.body(bytes);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/download")
|
||||
public ResponseEntity<byte[]> downloadReport(@PathVariable Long id) {
|
||||
ReportResponse report = reportService.getReportById(id);
|
||||
byte[] bytes = reportService.getReportBytes(id);
|
||||
return ResponseEntity.ok()
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.header("Content-Disposition", "attachment; filename=\"" + report.getFileName() + "\"")
|
||||
.body(bytes);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/pdf")
|
||||
public ResponseEntity<byte[]> getReportAsPdf(@PathVariable Long id) {
|
||||
byte[] pdfBytes = reportService.convertReportToPdf(id);
|
||||
return ResponseEntity.ok()
|
||||
.header("Content-Type", "application/pdf")
|
||||
.header("Content-Disposition", "inline; filename=preview.pdf")
|
||||
.body(pdfBytes);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<ReportResponse> uploadReport(
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@RequestParam("projectId") Long projectId,
|
||||
@RequestParam("fileType") String fileType) {
|
||||
System.err.println("=== UPLOAD CALLED: file=" + file + " projectId=" + projectId + " fileType=" + fileType);
|
||||
try {
|
||||
String filename = file.getOriginalFilename();
|
||||
if (filename != null) {
|
||||
String extension = getFileExtension(filename).toLowerCase();
|
||||
if (!isValidExtension(extension)) {
|
||||
return ResponseEntity.badRequest().body(null);
|
||||
}
|
||||
}
|
||||
ReportResponse response = reportService.uploadReport(file, projectId, fileType);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(response);
|
||||
} catch (Exception e) {
|
||||
System.err.println("=== UPLOAD EXCEPTION: " + e.getClass().getName() + ": " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).body(new ReportResponse(null, projectId, "ERROR", fileType, "ERROR", null, "ERROR:" + e.getClass().getSimpleName() + ":" + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<ReportResponse> updateReport(
|
||||
@PathVariable Long id,
|
||||
@RequestBody ReportRequest request) {
|
||||
return ResponseEntity.ok(reportService.updateReport(id, request));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> deleteReport(@PathVariable Long id) {
|
||||
reportService.deleteReport(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
private String getFileExtension(String filename) {
|
||||
int lastDotIndex = filename.lastIndexOf('.');
|
||||
if (lastDotIndex > 0) {
|
||||
return filename.substring(lastDotIndex + 1);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private boolean isValidExtension(String extension) {
|
||||
return extension.equals("html") || extension.equals("md") ||
|
||||
extension.equals("ppt") || extension.equals("pptx") || extension.equals("pdf");
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<?> handleError(Exception e) {
|
||||
String msg = e.getClass().getSimpleName() + ": " + e.getMessage();
|
||||
System.err.println("CONTROLLER ERROR: " + msg);
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).body(new java.util.LinkedHashMap<String,String>() {{
|
||||
put("error", msg);
|
||||
put("type", e.getClass().getName());
|
||||
}});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.reportdist.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class ProjectRequest {
|
||||
|
||||
@NotBlank(message = "Project name is required")
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private String coverImage;
|
||||
|
||||
public ProjectRequest() {}
|
||||
|
||||
public ProjectRequest(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public String getCoverImage() { return coverImage; }
|
||||
public void setCoverImage(String coverImage) { this.coverImage = coverImage; }
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.reportdist.dto;
|
||||
|
||||
import com.reportdist.entity.Project;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class ProjectResponse {
|
||||
|
||||
private Long id;
|
||||
private String name;
|
||||
private String description;
|
||||
private LocalDateTime createdAt;
|
||||
private String coverImage;
|
||||
private long reportCount;
|
||||
private long todayNewReports;
|
||||
|
||||
public ProjectResponse() {}
|
||||
|
||||
public ProjectResponse(Long id, String name, String description, LocalDateTime createdAt) {
|
||||
this(id, name, description, createdAt, null, 0, 0);
|
||||
}
|
||||
|
||||
public ProjectResponse(Long id, String name, String description, LocalDateTime createdAt, String coverImage) {
|
||||
this(id, name, description, createdAt, coverImage, 0, 0);
|
||||
}
|
||||
|
||||
public ProjectResponse(Long id, String name, String description, LocalDateTime createdAt, String coverImage, long reportCount) {
|
||||
this(id, name, description, createdAt, coverImage, reportCount, 0);
|
||||
}
|
||||
|
||||
public ProjectResponse(Long id, String name, String description, LocalDateTime createdAt, String coverImage, long reportCount, long todayNewReports) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.createdAt = createdAt;
|
||||
this.coverImage = coverImage;
|
||||
this.reportCount = reportCount;
|
||||
this.todayNewReports = todayNewReports;
|
||||
}
|
||||
|
||||
public static ProjectResponse fromEntity(Project project) {
|
||||
return fromEntity(project, 0, 0);
|
||||
}
|
||||
|
||||
public static ProjectResponse fromEntity(Project project, long reportCount) {
|
||||
return fromEntity(project, reportCount, 0);
|
||||
}
|
||||
|
||||
public static ProjectResponse fromEntity(Project project, long reportCount, long todayNewReports) {
|
||||
return new ProjectResponse(
|
||||
project.getId(),
|
||||
project.getName(),
|
||||
project.getDescription(),
|
||||
project.getCreatedAt(),
|
||||
project.getCoverImage(),
|
||||
reportCount,
|
||||
todayNewReports
|
||||
);
|
||||
}
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||
|
||||
public String getCoverImage() { return coverImage; }
|
||||
public void setCoverImage(String coverImage) { this.coverImage = coverImage; }
|
||||
|
||||
public long getReportCount() { return reportCount; }
|
||||
public void setReportCount(long reportCount) { this.reportCount = reportCount; }
|
||||
|
||||
public long getTodayNewReports() { return todayNewReports; }
|
||||
public void setTodayNewReports(long todayNewReports) { this.todayNewReports = todayNewReports; }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.reportdist.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class ReportRequest {
|
||||
|
||||
@NotBlank(message = "File name is required")
|
||||
private String fileName;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String fileType;
|
||||
|
||||
public ReportRequest() {}
|
||||
|
||||
public ReportRequest(String fileName, Long projectId, String fileType) {
|
||||
this.fileName = fileName;
|
||||
this.projectId = projectId;
|
||||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
public String getFileName() { return fileName; }
|
||||
public void setFileName(String fileName) { this.fileName = fileName; }
|
||||
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
|
||||
public String getFileType() { return fileType; }
|
||||
public void setFileType(String fileType) { this.fileType = fileType; }
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.reportdist.dto;
|
||||
|
||||
import com.reportdist.entity.Report;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class ReportResponse {
|
||||
|
||||
private Long id;
|
||||
private Long projectId;
|
||||
private String fileName;
|
||||
private String fileType;
|
||||
private String filePath;
|
||||
private LocalDateTime uploadTime;
|
||||
private String fileContent;
|
||||
|
||||
public ReportResponse() {}
|
||||
|
||||
public ReportResponse(Long id, Long projectId, String fileName, String fileType, String filePath, LocalDateTime uploadTime, String fileContent) {
|
||||
this.id = id;
|
||||
this.projectId = projectId;
|
||||
this.fileName = fileName;
|
||||
this.fileType = fileType;
|
||||
this.filePath = filePath;
|
||||
this.uploadTime = uploadTime;
|
||||
this.fileContent = fileContent;
|
||||
}
|
||||
|
||||
public static ReportResponse fromEntity(Report report) {
|
||||
return new ReportResponse(
|
||||
report.getId(),
|
||||
report.getProjectId(),
|
||||
report.getFileName(),
|
||||
report.getFileType().name(),
|
||||
report.getFilePath(),
|
||||
report.getUploadTime(),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public static ReportResponse fromEntityWithContent(Report report, String fileContent) {
|
||||
return new ReportResponse(
|
||||
report.getId(),
|
||||
report.getProjectId(),
|
||||
report.getFileName(),
|
||||
report.getFileType().name(),
|
||||
report.getFilePath(),
|
||||
report.getUploadTime(),
|
||||
fileContent
|
||||
);
|
||||
}
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
|
||||
public String getFileName() { return fileName; }
|
||||
public void setFileName(String fileName) { this.fileName = fileName; }
|
||||
|
||||
public String getFileType() { return fileType; }
|
||||
public void setFileType(String fileType) { this.fileType = fileType; }
|
||||
|
||||
public String getFilePath() { return filePath; }
|
||||
public void setFilePath(String filePath) { this.filePath = filePath; }
|
||||
|
||||
public LocalDateTime getUploadTime() { return uploadTime; }
|
||||
public void setUploadTime(LocalDateTime uploadTime) { this.uploadTime = uploadTime; }
|
||||
|
||||
public String getFileContent() { return fileContent; }
|
||||
public void setFileContent(String fileContent) { this.fileContent = fileContent; }
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.reportdist.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "projects")
|
||||
public class Project {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@Column(name = "created_at", nullable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private String coverImage;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
createdAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Project() {}
|
||||
|
||||
public Project(Long id, String name, String description, LocalDateTime createdAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||
|
||||
public String getCoverImage() { return coverImage; }
|
||||
public void setCoverImage(String coverImage) { this.coverImage = coverImage; }
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.reportdist.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "reports")
|
||||
public class Report {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "project_id", nullable = false)
|
||||
private Long projectId;
|
||||
|
||||
@Column(name = "file_name", nullable = false)
|
||||
private String fileName;
|
||||
|
||||
@Column(name = "file_type", nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private FileType fileType;
|
||||
|
||||
@Column(name = "file_path", nullable = false)
|
||||
private String filePath;
|
||||
|
||||
@Column(name = "upload_time", nullable = false)
|
||||
private LocalDateTime uploadTime;
|
||||
|
||||
@Column(name = "pdf_path")
|
||||
private String pdfPath;
|
||||
|
||||
@Column(name = "pdf_ready", nullable = false)
|
||||
private boolean pdfReady = false;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
uploadTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public enum FileType {
|
||||
HTML, MD, PPT, PPTX, PDF
|
||||
}
|
||||
|
||||
public Report() {}
|
||||
|
||||
public Report(Long id, Long projectId, String fileName, FileType fileType, String filePath, LocalDateTime uploadTime) {
|
||||
this.id = id;
|
||||
this.projectId = projectId;
|
||||
this.fileName = fileName;
|
||||
this.fileType = fileType;
|
||||
this.filePath = filePath;
|
||||
this.uploadTime = uploadTime;
|
||||
}
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
|
||||
public String getFileName() { return fileName; }
|
||||
public void setFileName(String fileName) { this.fileName = fileName; }
|
||||
|
||||
public FileType getFileType() { return fileType; }
|
||||
public void setFileType(FileType fileType) { this.fileType = fileType; }
|
||||
|
||||
public String getFilePath() { return filePath; }
|
||||
public void setFilePath(String filePath) { this.filePath = filePath; }
|
||||
|
||||
public LocalDateTime getUploadTime() { return uploadTime; }
|
||||
public void setUploadTime(LocalDateTime uploadTime) { this.uploadTime = uploadTime; }
|
||||
|
||||
public String getPdfPath() { return pdfPath; }
|
||||
public void setPdfPath(String pdfPath) { this.pdfPath = pdfPath; }
|
||||
|
||||
public boolean isPdfReady() { return pdfReady; }
|
||||
public void setPdfReady(boolean pdfReady) { this.pdfReady = pdfReady; }
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.reportdist.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(MultipartException.class)
|
||||
public ResponseEntity<?> handleMultipart(MultipartException ex) {
|
||||
System.err.println("=== MULTIPART ERROR ===");
|
||||
ex.printStackTrace();
|
||||
return ResponseEntity.status(500).body(java.util.Map.of(
|
||||
"error", "MULTIPART:" + ex.getClass().getSimpleName() + ":" + ex.getMessage(),
|
||||
"type", ex.getClass().getName()
|
||||
));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MaxUploadSizeExceededException.class)
|
||||
public ResponseEntity<?> handleSize(MaxUploadSizeExceededException ex) {
|
||||
System.err.println("=== SIZE ERROR ===");
|
||||
ex.printStackTrace();
|
||||
return ResponseEntity.status(500).body(java.util.Map.of(
|
||||
"error", "SIZE_LIMIT:" + ex.getMessage(),
|
||||
"type", ex.getClass().getName()
|
||||
));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public ResponseEntity<?> handleTypeMismatch(MethodArgumentTypeMismatchException ex) {
|
||||
System.err.println("=== TYPE MISMATCH ===");
|
||||
ex.printStackTrace();
|
||||
return ResponseEntity.status(400).body(java.util.Map.of(
|
||||
"error", "TYPE_MISMATCH:" + ex.getName() + " cannot parse " + ex.getValue(),
|
||||
"type", ex.getClass().getName()
|
||||
));
|
||||
}
|
||||
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public ResponseEntity<?> handleRuntimeException(RuntimeException ex) {
|
||||
System.err.println("=== RUNTIME ERROR ===");
|
||||
ex.printStackTrace();
|
||||
return ResponseEntity.status(500).body(java.util.Map.of(
|
||||
"error", ex.getMessage() != null ? ex.getMessage() : "null",
|
||||
"type", ex.getClass().getName()
|
||||
));
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<?> handleGeneric(Exception ex) {
|
||||
System.err.println("=== GENERIC ERROR ===");
|
||||
ex.printStackTrace();
|
||||
return ResponseEntity.status(500).body(java.util.Map.of(
|
||||
"error", ex.getClass().getSimpleName() + ":" + ex.getMessage(),
|
||||
"type", ex.getClass().getName()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.reportdist.repository;
|
||||
|
||||
import com.reportdist.entity.Project;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface ProjectRepository extends JpaRepository<Project, Long> {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.reportdist.repository;
|
||||
|
||||
import com.reportdist.entity.Report;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ReportRepository extends JpaRepository<Report, Long> {
|
||||
|
||||
List<Report> findByProjectId(Long projectId);
|
||||
long countByProjectId(Long projectId);
|
||||
long countByUploadTimeAfter(LocalDateTime time);
|
||||
long countByProjectIdAndUploadTimeAfter(Long projectId, LocalDateTime time);
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.reportdist.service;
|
||||
|
||||
import org.apache.poi.xslf.usermodel.XMLSlideShow;
|
||||
import org.apache.poi.xslf.usermodel.XSLFSlide;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||
import org.apache.pdfbox.rendering.RenderDestination;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class PptxToPdfService {
|
||||
|
||||
public static byte[] convert(String pptxFilePath) throws IOException {
|
||||
Path path = Path.of(pptxFilePath);
|
||||
if (!Files.exists(path)) {
|
||||
throw new IOException("PPTX file not found: " + pptxFilePath);
|
||||
}
|
||||
|
||||
try (InputStream is = Files.newInputStream(path);
|
||||
XMLSlideShow pptx = new XMLSlideShow(is)) {
|
||||
|
||||
List<XSLFSlide> slides = pptx.getSlides();
|
||||
if (slides.isEmpty()) {
|
||||
throw new IOException("No slides found in PPTX file");
|
||||
}
|
||||
|
||||
Dimension slideSize = pptx.getPageSize();
|
||||
int scale = 2;
|
||||
int imgWidth = (int) slideSize.getWidth() * scale;
|
||||
int imgHeight = (int) slideSize.getHeight() * scale;
|
||||
|
||||
// First render all slides to PNG images
|
||||
byte[][] slideImages = new byte[slides.size()][];
|
||||
for (int i = 0; i < slides.size(); i++) {
|
||||
XSLFSlide slide = slides.get(i);
|
||||
|
||||
BufferedImage slideImage = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D graphics = slideImage.createGraphics();
|
||||
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fillRect(0, 0, imgWidth, imgHeight);
|
||||
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
|
||||
|
||||
graphics.scale(scale, scale);
|
||||
|
||||
try {
|
||||
slide.draw(graphics);
|
||||
} catch (Exception e) {
|
||||
drawTextBoxes(graphics, slide);
|
||||
}
|
||||
|
||||
graphics.dispose();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(slideImage, "PNG", baos);
|
||||
slideImages[i] = baos.toByteArray();
|
||||
}
|
||||
|
||||
// Create PDF with each slide as a page
|
||||
PDDocument document = new PDDocument();
|
||||
|
||||
for (int i = 0; i < slideImages.length; i++) {
|
||||
byte[] pngBytes = slideImages[i];
|
||||
|
||||
// Calculate page size to match slide aspect ratio
|
||||
float imgAspect = (float) imgWidth / imgHeight;
|
||||
float pageWidth = PDRectangle.A4.getWidth();
|
||||
float pageHeight = pageWidth / imgAspect;
|
||||
|
||||
if (pageHeight > PDRectangle.A4.getHeight()) {
|
||||
pageHeight = PDRectangle.A4.getHeight();
|
||||
pageWidth = pageHeight * imgAspect;
|
||||
}
|
||||
|
||||
PDPage page = new PDPage(new PDRectangle(pageWidth, pageHeight));
|
||||
document.addPage(page);
|
||||
|
||||
// Draw image on page
|
||||
try (PDPageContentStream cs = new PDPageContentStream(document, page)) {
|
||||
PDImageXObject pdImage = PDImageXObject.createFromByteArray(document, pngBytes, "slide_" + i);
|
||||
cs.drawImage(pdImage, 0, 0, pageWidth, pageHeight);
|
||||
}
|
||||
}
|
||||
|
||||
ByteArrayOutputStream pdfBaos = new ByteArrayOutputStream();
|
||||
document.save(pdfBaos);
|
||||
document.close();
|
||||
|
||||
return pdfBaos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawTextBoxes(Graphics2D g, XSLFSlide slide) {
|
||||
for (org.apache.poi.xslf.usermodel.XSLFShape shape : slide.getShapes()) {
|
||||
if (shape instanceof org.apache.poi.xslf.usermodel.XSLFTextShape) {
|
||||
org.apache.poi.xslf.usermodel.XSLFTextShape ts = (org.apache.poi.xslf.usermodel.XSLFTextShape) shape;
|
||||
java.awt.geom.Rectangle2D rect = ts.getAnchor();
|
||||
if (rect == null) continue;
|
||||
|
||||
g.setColor(Color.WHITE);
|
||||
g.fill(rect);
|
||||
|
||||
g.setColor(Color.LIGHT_GRAY);
|
||||
g.draw(rect);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (org.apache.poi.xslf.usermodel.XSLFTextParagraph para : ts.getTextParagraphs()) {
|
||||
for (org.apache.poi.xslf.usermodel.XSLFTextRun run : para.getTextRuns()) {
|
||||
String text = run.getRawText();
|
||||
if (text != null) sb.append(text);
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
String text = sb.toString().trim();
|
||||
if (!text.isEmpty()) {
|
||||
g.setColor(Color.BLACK);
|
||||
g.setFont(new Font("Arial", Font.PLAIN, 10));
|
||||
int x = (int) rect.getX() + 5;
|
||||
int y = (int) rect.getY() + 15;
|
||||
for (String line : text.split("\n")) {
|
||||
g.drawString(line, x, y);
|
||||
y += 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.reportdist.service;
|
||||
|
||||
import com.reportdist.dto.ProjectRequest;
|
||||
import com.reportdist.dto.ProjectResponse;
|
||||
import com.reportdist.entity.Project;
|
||||
import com.reportdist.repository.ProjectRepository;
|
||||
import com.reportdist.repository.ReportRepository;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
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.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class ProjectService {
|
||||
|
||||
private final ProjectRepository projectRepository;
|
||||
private final ReportRepository reportRepository;
|
||||
private String uploadDir;
|
||||
|
||||
public ProjectService(ProjectRepository projectRepository, ReportRepository reportRepository) {
|
||||
this.projectRepository = projectRepository;
|
||||
this.reportRepository = reportRepository;
|
||||
}
|
||||
|
||||
@Value("${file.upload.dir}")
|
||||
public void setUploadDir(String uploadDir) {
|
||||
this.uploadDir = uploadDir;
|
||||
}
|
||||
|
||||
public List<ProjectResponse> getAllProjects() {
|
||||
LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay();
|
||||
return projectRepository.findAll().stream()
|
||||
.map(project -> {
|
||||
long count = reportRepository.countByProjectId(project.getId());
|
||||
long todayNew = reportRepository.countByProjectIdAndUploadTimeAfter(project.getId(), startOfDay);
|
||||
return ProjectResponse.fromEntity(project, count, todayNew);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public ProjectResponse getProjectById(Long id) {
|
||||
Project project = projectRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Project not found with id: " + id));
|
||||
LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay();
|
||||
long count = reportRepository.countByProjectId(id);
|
||||
long todayNew = reportRepository.countByProjectIdAndUploadTimeAfter(id, startOfDay);
|
||||
return ProjectResponse.fromEntity(project, count, todayNew);
|
||||
}
|
||||
|
||||
public ProjectResponse createProject(ProjectRequest request) {
|
||||
Project project = new Project();
|
||||
project.setName(request.getName());
|
||||
project.setDescription(request.getDescription());
|
||||
if (request.getCoverImage() != null) {
|
||||
project.setCoverImage(request.getCoverImage());
|
||||
}
|
||||
Project saved = projectRepository.save(project);
|
||||
return ProjectResponse.fromEntity(saved, 0);
|
||||
}
|
||||
|
||||
public ProjectResponse updateProject(Long id, String name, String description, MultipartFile coverImage) {
|
||||
Project project = projectRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Project not found with id: " + id));
|
||||
|
||||
if (name != null) {
|
||||
project.setName(name);
|
||||
}
|
||||
if (description != null) {
|
||||
project.setDescription(description);
|
||||
}
|
||||
if (coverImage != null && !coverImage.isEmpty()) {
|
||||
try {
|
||||
// Save cover image to uploads/covers/{projectId}/
|
||||
Path coverDir = Paths.get(uploadDir, "covers", String.valueOf(id));
|
||||
Files.createDirectories(coverDir);
|
||||
|
||||
String originalFilename = coverImage.getOriginalFilename();
|
||||
String extension = "";
|
||||
if (originalFilename != null && originalFilename.contains(".")) {
|
||||
extension = originalFilename.substring(originalFilename.lastIndexOf("."));
|
||||
}
|
||||
String uniqueFileName = UUID.randomUUID().toString() + extension;
|
||||
Path coverPath = coverDir.resolve(uniqueFileName);
|
||||
|
||||
Files.write(coverPath, coverImage.getBytes());
|
||||
|
||||
// Store relative path for serving
|
||||
String coverUrl = "/uploads/covers/" + id + "/" + uniqueFileName;
|
||||
project.setCoverImage(coverUrl);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to save cover image: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
Project updated = projectRepository.save(project);
|
||||
long count = reportRepository.countByProjectId(id);
|
||||
long todayNew = reportRepository.countByProjectIdAndUploadTimeAfter(id, LocalDateTime.now().toLocalDate().atStartOfDay());
|
||||
return ProjectResponse.fromEntity(updated, count, todayNew);
|
||||
}
|
||||
|
||||
// Keep old method for backward compatibility
|
||||
public ProjectResponse updateProject(Long id, ProjectRequest request) {
|
||||
return updateProject(id, request.getName(), request.getDescription(), null);
|
||||
}
|
||||
|
||||
public void deleteProject(Long id) {
|
||||
if (!projectRepository.existsById(id)) {
|
||||
throw new RuntimeException("Project not found with id: " + id);
|
||||
}
|
||||
projectRepository.deleteById(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package com.reportdist.service;
|
||||
|
||||
import com.reportdist.dto.ReportRequest;
|
||||
import com.reportdist.dto.ReportResponse;
|
||||
import com.reportdist.entity.Report;
|
||||
import com.reportdist.repository.ReportRepository;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
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.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class ReportService {
|
||||
|
||||
private final ReportRepository reportRepository;
|
||||
private String uploadDir;
|
||||
|
||||
public ReportService(ReportRepository reportRepository) {
|
||||
this.reportRepository = reportRepository;
|
||||
}
|
||||
|
||||
@Value("${file.upload.dir}")
|
||||
public void setUploadDir(String uploadDir) {
|
||||
this.uploadDir = uploadDir;
|
||||
}
|
||||
|
||||
public List<ReportResponse> getAllReports(Long projectId) {
|
||||
List<Report> reports;
|
||||
if (projectId != null) {
|
||||
reports = reportRepository.findByProjectId(projectId);
|
||||
} else {
|
||||
reports = reportRepository.findAll();
|
||||
}
|
||||
return reports.stream()
|
||||
.map(ReportResponse::fromEntity)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public ReportResponse getReportById(Long id) {
|
||||
Report report = reportRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Report not found with id: " + id));
|
||||
|
||||
String fileContent = readFileContent(report.getFilePath());
|
||||
return ReportResponse.fromEntityWithContent(report, fileContent);
|
||||
}
|
||||
|
||||
public ReportResponse uploadReport(MultipartFile file, Long projectId, String fileType) {
|
||||
try {
|
||||
// Create project subdirectory if needed
|
||||
Path projectDir = Paths.get(uploadDir, String.valueOf(projectId));
|
||||
Files.createDirectories(projectDir);
|
||||
|
||||
// Generate unique filename
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String uniqueFileName = System.currentTimeMillis() + "_" + originalFilename;
|
||||
Path filePath = projectDir.resolve(uniqueFileName);
|
||||
|
||||
// Save file
|
||||
file.transferTo(filePath.toFile());
|
||||
|
||||
// Create report entity
|
||||
Report report = new Report();
|
||||
report.setProjectId(projectId);
|
||||
report.setFileName(originalFilename);
|
||||
report.setFileType(Report.FileType.valueOf(fileType.toUpperCase()));
|
||||
report.setFilePath(filePath.toString());
|
||||
report.setPdfReady(false);
|
||||
|
||||
// Pre-render PDF for PPTX files
|
||||
if (fileType.equalsIgnoreCase("pptx") || fileType.equalsIgnoreCase("ppt")) {
|
||||
try {
|
||||
byte[] pdfBytes = PptxToPdfService.convert(filePath.toString());
|
||||
Path pdfDir = projectDir.resolve("pdfs");
|
||||
Files.createDirectories(pdfDir);
|
||||
String pdfFileName = uniqueFileName.replaceAll("\\.(pptx?|PPT|X)$", ".pdf");
|
||||
Path pdfPath = pdfDir.resolve(pdfFileName);
|
||||
Files.write(pdfPath, pdfBytes);
|
||||
report.setPdfPath(pdfPath.toString());
|
||||
report.setPdfReady(true);
|
||||
System.out.println("PDF pre-rendered successfully: " + pdfPath);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to pre-render PDF: " + e.getMessage());
|
||||
// Continue without PDF - not critical
|
||||
}
|
||||
}
|
||||
|
||||
Report saved = reportRepository.save(report);
|
||||
return ReportResponse.fromEntity(saved);
|
||||
} catch (IOException e) {
|
||||
System.err.println("=== UPLOAD ERROR ===");
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("UPLOAD_FAILED:" + e.getClass().getName() + ":" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public ReportResponse updateReport(Long id, ReportRequest request) {
|
||||
Report report = reportRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Report not found with id: " + id));
|
||||
|
||||
if (request.getFileName() != null) {
|
||||
report.setFileName(request.getFileName());
|
||||
}
|
||||
if (request.getProjectId() != null) {
|
||||
report.setProjectId(request.getProjectId());
|
||||
}
|
||||
if (request.getFileType() != null) {
|
||||
report.setFileType(Report.FileType.valueOf(request.getFileType().toUpperCase()));
|
||||
}
|
||||
|
||||
Report updated = reportRepository.save(report);
|
||||
return ReportResponse.fromEntity(updated);
|
||||
}
|
||||
|
||||
public void deleteReport(Long id) {
|
||||
Report report = reportRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Report not found with id: " + id));
|
||||
|
||||
// Delete the file
|
||||
try {
|
||||
Path filePath = Paths.get(report.getFilePath());
|
||||
Files.deleteIfExists(filePath);
|
||||
} catch (IOException e) {
|
||||
// Log but don't fail the delete operation
|
||||
System.err.println("Failed to delete file: " + e.getMessage());
|
||||
}
|
||||
|
||||
reportRepository.deleteById(id);
|
||||
}
|
||||
|
||||
private String readFileContent(String filePath) {
|
||||
try {
|
||||
Path path = Paths.get(filePath);
|
||||
if (Files.exists(path)) {
|
||||
return Files.readString(path);
|
||||
}
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getReportBytes(Long id) {
|
||||
Report report = reportRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Report not found with id: " + id));
|
||||
try {
|
||||
return Files.readAllBytes(Paths.get(report.getFilePath()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to read file: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] convertReportToPdf(Long id) {
|
||||
Report report = reportRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Report not found with id: " + id));
|
||||
|
||||
String fileType = report.getFileType().name().toLowerCase();
|
||||
if (!fileType.equals("pptx") && !fileType.equals("ppt")) {
|
||||
throw new RuntimeException("Only PPTX files can be converted to PDF. Current file type: " + fileType);
|
||||
}
|
||||
|
||||
// Return pre-rendered PDF if available
|
||||
if (report.isPdfReady() && report.getPdfPath() != null) {
|
||||
try {
|
||||
Path pdfPath = Paths.get(report.getPdfPath());
|
||||
if (Files.exists(pdfPath)) {
|
||||
return Files.readAllBytes(pdfPath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to read pre-rendered PDF: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: render on demand
|
||||
try {
|
||||
return PptxToPdfService.convert(report.getFilePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to convert PPTX to PDF: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: daily-report-distribution
|
||||
|
||||
datasource:
|
||||
url: jdbc:sqlite:./database.db
|
||||
driver-class-name: org.sqlite.JDBC
|
||||
|
||||
jpa:
|
||||
database-platform: org.hibernate.community.dialect.SQLiteDialect
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
show-sql: false
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
|
||||
servlet:
|
||||
multipart:
|
||||
enabled: true
|
||||
max-file-size: 100MB
|
||||
max-request-size: 100MB
|
||||
|
||||
# Serve uploaded files statically
|
||||
web:
|
||||
resources:
|
||||
static-locations: file:./uploads/
|
||||
|
||||
file:
|
||||
upload:
|
||||
dir: ${UPLOAD_DIR:${user.dir}/uploads}
|
||||
|
||||
# Spring Boot Actuator (Docker healthcheck)
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,health-liveness,health-readiness
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
probes:
|
||||
enabled: true
|
||||
Reference in New Issue
Block a user