7 Commits

Author SHA1 Message Date
3b85b12741 Few improvements such as
application.properties.prod file
new DTO for response when adding notice
2025-05-02 14:49:22 +02:00
039678b90a Categories as map 2025-04-28 18:37:41 +02:00
025f733362 added endpoint for variables 2025-04-28 15:58:59 +02:00
c2f74ab799 little fixes one more time 2025-04-28 15:44:35 +02:00
bf565178f6 fix of images deletion 2025-04-28 15:06:32 +02:00
7f8f13b115 Images are working but still there is need to add isImageMain flag to images. 2025-04-28 14:17:38 +02:00
6b5dded7f8 WIP for images 2025-04-24 07:34:53 +02:00
21 changed files with 543 additions and 141 deletions

View File

@@ -1,5 +1,7 @@
FROM openjdk:21
COPY target/ArtisanConnectBackend-0.0.1-SNAPSHOT.jar app.jar
WORKDIR /app
ENTRYPOINT ["java","-jar","/app.jar"]
COPY target/ArtisanConnectBackend-0.0.1-SNAPSHOT.jar app/artisan.jar
ENTRYPOINT ["java","-jar","app/artisan.jar"]

View File

@@ -2,10 +2,13 @@ services:
app:
container_name: artisan
build: .
networks:
- artisan_network
depends_on:
- db
networks:
- artisan_network
ports:
- '8085:8080'
db:
container_name: db

View File

@@ -93,6 +93,11 @@
<artifactId>jakarta.validation-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springdoc</groupId>-->
<!-- <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>-->
<!-- <version>2.8.5</version>-->
<!-- </dependency>-->
</dependencies>
<build>

View File

@@ -1,13 +1,27 @@
package _11.asktpk.artisanconnectbackend;
import jakarta.annotation.PostConstruct;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
@SpringBootApplication
public class ArtisanConnectBackendApplication {
private final Environment environment;
public ArtisanConnectBackendApplication(Environment environment) {
this.environment = environment;
}
public static void main(String[] args) {
SpringApplication.run(ArtisanConnectBackendApplication.class, args);
}
@PostConstruct
public void logDataSourceUrl() {
System.out.println("Datasource URL: " + environment.getProperty("spring.datasource.url"));
}
}

View File

@@ -0,0 +1,35 @@
package _11.asktpk.artisanconnectbackend.config;
import _11.asktpk.artisanconnectbackend.dto.RequestResponseDTO;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
@Controller
public class CustomErrorController implements ErrorController {
@RequestMapping("/error")
public ResponseEntity<RequestResponseDTO> handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
int statusCode = Integer.parseInt(status.toString());
if (statusCode == HttpStatus.NOT_FOUND.value()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new RequestResponseDTO("Nie znaleziono zasobu. Sprawdź URL i spróbuj ponownie."));
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new RequestResponseDTO("Wystąpił wewnętrzny błąd serwera. Spróbuj ponownie później."));
}
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new RequestResponseDTO("Wystąpił nieoczekiwany błąd."));
}
}

View File

@@ -0,0 +1,93 @@
package _11.asktpk.artisanconnectbackend.controller;
import _11.asktpk.artisanconnectbackend.dto.RequestResponseDTO;
import _11.asktpk.artisanconnectbackend.service.ImageService;
import _11.asktpk.artisanconnectbackend.service.NoticeService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
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.*;
@RestController
@RequestMapping("/api/v1/images")
public class ImageController {
private final ImageService imageService;
private final NoticeService noticeService;
ImageController(ImageService imageService, NoticeService noticeService) {
this.imageService = imageService;
this.noticeService = noticeService;
}
@Value("${file.upload-dir}")
private String uploadDir;
@PostMapping("/upload/{id}")
public ResponseEntity<RequestResponseDTO> uploadImage(@RequestParam("file") MultipartFile file, @PathVariable("id") Long noticeId) {
try {
if(file.isEmpty()) {
return ResponseEntity.badRequest().body(new RequestResponseDTO("File is empty"));
}
if(!Objects.equals(file.getContentType(), "image/jpeg") && !Objects.equals(file.getContentType(), "image/png")) {
return ResponseEntity.badRequest().body(new RequestResponseDTO("File must be a JPEG or PNG image."));
}
if(noticeId == null || !noticeService.noticeExists(noticeId)) {
return ResponseEntity.badRequest().body(new RequestResponseDTO("Notice ID is invalid or does not exist."));
}
String newImageName = imageService.saveImageToStorage(uploadDir, file);
imageService.addImageNameToDB(newImageName, noticeId);
return ResponseEntity.ok(new RequestResponseDTO("Image uploaded successfully with new name: " + newImageName));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).body(new RequestResponseDTO(e.getMessage()));
}
}
@GetMapping("/get/{filename}")
public ResponseEntity<Resource> getImage(@PathVariable String filename) {
try {
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_JPEG)
.body(imageService.getImage(uploadDir, filename));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
@GetMapping("/list/{id}")
public ResponseEntity<List<String>> getImagesNamesList(@PathVariable("id") Long noticeId) {
if(noticeId == null) {
return ResponseEntity.badRequest().body(Collections.singletonList("Notice ID is invalid or does not exist."));
}
List<String> result;
try {
result = imageService.getImagesList(noticeId);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Collections.singletonList(e.getMessage()));
}
return ResponseEntity.ok(result);
}
@DeleteMapping("/delete/{filename}")
public ResponseEntity<RequestResponseDTO> deleteImage(@PathVariable("filename") String filename) {
if(filename == null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new RequestResponseDTO("Filename is empty."));
}
try {
imageService.deleteImage(uploadDir, filename);
return ResponseEntity.status(HttpStatus.OK).body(new RequestResponseDTO("Image deleted successfully."));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new RequestResponseDTO(e.getMessage()));
}
}
}

View File

@@ -1,5 +1,6 @@
package _11.asktpk.artisanconnectbackend.controller;
import _11.asktpk.artisanconnectbackend.dto.NoticeAdditionDTO;
import _11.asktpk.artisanconnectbackend.service.ClientService;
import _11.asktpk.artisanconnectbackend.service.NoticeService;
import _11.asktpk.artisanconnectbackend.dto.NoticeDTO;
@@ -7,11 +8,7 @@ import jakarta.persistence.EntityNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
@@ -41,18 +38,22 @@ public class NoticeController {
}
@PostMapping("/add")
public ResponseEntity<String> addNotice(@RequestBody NoticeDTO dto) {
public ResponseEntity<NoticeAdditionDTO> addNotice(@RequestBody NoticeDTO dto) {
if (!clientService.clientExists(dto.getClientId())) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body("Nie znaleziono klienta o ID: " + dto.getClientId());
.body(new NoticeAdditionDTO("Nie znaleziono klienta o ID: " + dto.getClientId()));
}
if (dto.getCategory() == null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new NoticeAdditionDTO("Nie ma takiej kategorii"));
}
dto.setPublishDate(java.time.LocalDateTime.now());
noticeService.addNotice(dto);
Long newNoticeId = noticeService.addNotice(dto);
return ResponseEntity.status(HttpStatus.CREATED).body("Dodano ogłoszenie.");
return ResponseEntity.status(HttpStatus.CREATED).body(new NoticeAdditionDTO(newNoticeId ,"Dodano ogłoszenie."));
}
@@ -107,53 +108,4 @@ public class NoticeController {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@PostMapping("/upload/{id}")
public ResponseEntity<String> uploadImage(@PathVariable("id") Long id, @RequestParam("file") MultipartFile file) {
if (!noticeService.noticeExists(id)) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Nie znaleziono ogłoszenia o ID: " + id);
}
try {
String filePath = noticeService.saveImage(id, file);
return ResponseEntity.ok("Zdjęcie zapisane pod ścieżką: " + filePath);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Błąd podczas zapisywania zdjęcia: " + e.getMessage());
}
}
@GetMapping("/images/{id}")
public ResponseEntity<List<String>> getAllImages(@PathVariable("id") Long id) {
try {
Path directoryPath = Paths.get("src/main/resources/static/images/notices/" + id);
if (!Files.exists(directoryPath) || !Files.isDirectory(directoryPath)) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
List<String> imagePaths = new ArrayList<>();
Files.list(directoryPath).forEach(file -> {
if (Files.isRegularFile(file) && Files.isReadable(file)) {
imagePaths.add(file.toString());
}
});
if (imagePaths.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
return ResponseEntity.ok(imagePaths);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}
// @GetMapping("/check/{id}")
// public ResponseEntity<String> checkNotice(@PathVariable("id") long id) {
// if (noticeService.noticeExists(id)) {
// return ResponseEntity.ok("Ogłoszenie o ID " + id + " istnieje.");
// } else {
// return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Nie znaleziono ogłoszenia o ID: " + id);
// }
// }
}

View File

@@ -0,0 +1,30 @@
package _11.asktpk.artisanconnectbackend.controller;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/vars")
public class VariablesController {
@GetMapping("/categories")
public Map<Enums.Category, String> getAllVariables() {
return Enums.categoryPL;
}
@GetMapping("/statuses")
public List<Enums.Status> getAllStatuses() {
return List.of(Enums.Status.values());
}
@GetMapping("/roles")
public List<Enums.Role> getAllRoles() {
return List.of(Enums.Role.values());
}
}

View File

@@ -0,0 +1,9 @@
package _11.asktpk.artisanconnectbackend.dto;
import org.springframework.core.io.Resource;
public class ImageRequestDTO {
public Resource image;
public Long noticeId;
public boolean isMainImage;
}

View File

@@ -0,0 +1,19 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class NoticeAdditionDTO {
public Long noticeId;
public String message;
public NoticeAdditionDTO(String message) {
this.message = message;
}
public NoticeAdditionDTO(Long noticeId, String message) {
this.noticeId = noticeId;
this.message = message;
}
}

View File

@@ -22,8 +22,6 @@ public class NoticeDTO {
private Enums.Category category;
private List<String> images;
private Enums.Status status;
private LocalDateTime publishDate;
@@ -33,19 +31,4 @@ public class NoticeDTO {
public NoticeDTO() {
}
public NoticeDTO(Long noticeId, String title, Long clientId, String description, Double price,
Enums.Category category, List<String> images, Enums.Status status,
LocalDateTime publishDate, List<AttributesNotice> attributesNotices) {
this.noticeId = noticeId;
this.title = title;
this.clientId = clientId;
this.description = description;
this.price = price;
this.category = category;
this.images = images;
this.status = status;
this.publishDate = publishDate;
this.attributesNotices = attributesNotices;
}
}

View File

@@ -0,0 +1,13 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class RequestResponseDTO {
public String message;
public RequestResponseDTO(String message) {
this.message = message;
}
}

View File

@@ -0,0 +1,23 @@
package _11.asktpk.artisanconnectbackend.entities;
import jakarta.persistence.*;
import jdk.jfr.BooleanFlag;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "images")
@Getter @Setter
public class Image {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long noticeId;
private String imageName;
@BooleanFlag
private boolean isMainImage;
}

View File

@@ -30,9 +30,6 @@ public class Notice {
@Enumerated(EnumType.STRING)
private Category category;
@ElementCollection
private List<String> images;
@Enumerated(EnumType.STRING)
private Status status;

View File

@@ -0,0 +1,14 @@
package _11.asktpk.artisanconnectbackend.repository;
import _11.asktpk.artisanconnectbackend.entities.Image;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ImageRepository extends JpaRepository<Image, Long> {
List<Image> findByNoticeId(Long noticeId);
boolean existsImageByImageNameEqualsIgnoreCase(String imageName);
void deleteByImageNameEquals(String imageName);
}

View File

@@ -0,0 +1,93 @@
package _11.asktpk.artisanconnectbackend.service;
import _11.asktpk.artisanconnectbackend.entities.Image;
import _11.asktpk.artisanconnectbackend.repository.ImageRepository;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
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.nio.file.StandardCopyOption;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
@Transactional
public class ImageService {
private final ImageRepository imageRepository;
ImageService(ImageRepository imageRepository) {
this.imageRepository = imageRepository;
}
public String saveImageToStorage(String uploadDirectory, MultipartFile imageFile) throws IOException {
String uniqueFileName = UUID.randomUUID() + imageFile.getOriginalFilename();
Path uploadPath = Path.of(uploadDirectory);
Path filePath = uploadPath.resolve(uniqueFileName);
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
Files.copy(imageFile.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
return uniqueFileName;
}
public void addImageNameToDB(String filename, Long noticeId) {
Image image = new Image();
image.setImageName(filename);
image.setNoticeId(noticeId);
imageRepository.save(image);
}
public Resource getImage(String imageDirectory, String imageName) throws IOException {
Path filePath = Paths.get(imageDirectory).resolve(imageName);
Resource resource = new UrlResource(filePath.toUri());
if(imageName.isEmpty() || imageDirectory.isEmpty()) {
throw new IOException("Filename or folder is empty. Please check your request and try again.");
}
if (!resource.exists()) {
throw new IOException("File not found");
}
return resource;
}
public void deleteImage(String imageDirectory, String imageName) throws IOException {
Path imagePath = Path.of(imageDirectory, imageName);
deleteImageRecordFromDB(imageName);
if (Files.exists(imagePath)) {
Files.delete(imagePath);
} else {
throw new IOException("File not found");
}
}
public List<String> getImagesList(Long noticeID) throws Exception {
List<Image> images = imageRepository.findByNoticeId(noticeID);
if (images.isEmpty()) {
throw new Exception("There is no images for this notice");
}
return images.stream()
.map(Image::getImageName)
.collect(Collectors.toList());
}
public void deleteImageRecordFromDB(String imageName) {
if(imageRepository.existsImageByImageNameEqualsIgnoreCase(imageName)) {
imageRepository.deleteByImageNameEquals(imageName);
}
}
}

View File

@@ -7,13 +7,7 @@ import _11.asktpk.artisanconnectbackend.repository.NoticeRepository;
import _11.asktpk.artisanconnectbackend.dto.NoticeDTO;
import jakarta.persistence.EntityNotFoundException;
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.ArrayList;
import java.util.List;
@@ -34,7 +28,6 @@ public class NoticeService {
notice.setDescription(dto.getDescription());
notice.setPrice(dto.getPrice());
notice.setCategory(dto.getCategory());
notice.setImages(dto.getImages());
notice.setStatus(dto.getStatus());
notice.setPublishDate(dto.getPublishDate());
notice.setAttributesNotices(dto.getAttributesNotices());
@@ -54,7 +47,6 @@ public class NoticeService {
dto.setDescription(notice.getDescription());
dto.setPrice(notice.getPrice());
dto.setCategory(notice.getCategory());
dto.setImages(notice.getImages());
dto.setStatus(notice.getStatus());
dto.setPublishDate(notice.getPublishDate());
dto.setAttributesNotices(notice.getAttributesNotices());
@@ -76,8 +68,8 @@ public class NoticeService {
return toDTO(notice);
}
public void addNotice(NoticeDTO dto) {
noticeRepository.save(fromDTO(dto));
public Long addNotice(NoticeDTO dto) {
return noticeRepository.save(fromDTO(dto)).getIdNotice();
}
public boolean noticeExists(Long id) {
@@ -92,7 +84,6 @@ public class NoticeService {
existingNotice.setDescription(dto.getDescription());
existingNotice.setPrice(dto.getPrice());
existingNotice.setCategory(dto.getCategory());
existingNotice.setImages(dto.getImages());
existingNotice.setStatus(dto.getStatus());
existingNotice.setAttributesNotices(dto.getAttributesNotices());
@@ -112,51 +103,4 @@ public class NoticeService {
throw new EntityNotFoundException("Nie znaleziono ogłoszenia o ID: " + id);
}
}
public String saveImage(Long noticeId, MultipartFile file) throws IOException {
String uploadDir = "src/main/resources/static/images/notices/" + noticeId;
Path uploadPath = Paths.get(uploadDir);
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
// szukanie nazwy pliku
String fileName = file.getOriginalFilename();
if (fileName != null) {
String extension = fileName.substring(fileName.lastIndexOf('.'));
String baseName = fileName.substring(0, fileName.lastIndexOf('.'));
List<Path> filesInDirectory = Files.list(uploadPath)
.filter(Files::isRegularFile)
.toList();
int maxNumber = filesInDirectory.stream()
.map(path -> path.getFileName().toString())
.filter(name -> name.startsWith(baseName) && name.endsWith(extension))
.map(name -> name.substring(baseName.length(), name.length() - extension.length()))
.filter(number -> number.matches("\\d+"))
.mapToInt(Integer::parseInt)
.max()
.orElse(0);
fileName = baseName + (maxNumber + 1) + extension;
}
//koniec szukania nazwy pliku
if(fileName == null) {
throw new IOException("Nie można znaleźć nazwy pliku");
}
Path filePath = uploadPath.resolve(fileName);
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
Notice notice = noticeRepository.findById(noticeId)
.orElseThrow(() -> new EntityNotFoundException("Nie znaleziono ogłoszenia o ID: " + noticeId));
List<String> images = notice.getImages();
images.add(filePath.toString());
notice.setImages(images);
noticeRepository.save(notice);
return filePath.toString();
}
}

View File

@@ -1,5 +1,7 @@
package _11.asktpk.artisanconnectbackend.utils;
import java.util.Map;
public class Enums {
public enum Role {
ADMIN, USER
@@ -13,6 +15,31 @@ public class Enums {
Knitting, Carpentry, Other
}
public static final Map<Category, String> categoryPL = Map.ofEntries(
Map.entry(Category.Handmade, "Rękodzieło"),
Map.entry(Category.Woodworking, "Stolarstwo"),
Map.entry(Category.Metalworking, "Obróbka metalu"),
Map.entry(Category.Ceramics, "Ceramika"),
Map.entry(Category.Textiles, "Tekstylia"),
Map.entry(Category.Jewelry, "Biżuteria"),
Map.entry(Category.Leatherwork, "Wyroby skórzane"),
Map.entry(Category.Painting, "Malarstwo"),
Map.entry(Category.Sculpture, "Rzeźbiarstwo"),
Map.entry(Category.Glasswork, "Szklarstwo"),
Map.entry(Category.Furniture, "Meble"),
Map.entry(Category.Restoration, "Renowacja"),
Map.entry(Category.Tailoring, "Krawiectwo"),
Map.entry(Category.Weaving, "Tkactwo"),
Map.entry(Category.Calligraphy, "Kaligrafia"),
Map.entry(Category.Pottery, "Garncarstwo"),
Map.entry(Category.Blacksmithing, "Kowalstwo"),
Map.entry(Category.Basketry, "Koszykarstwo"),
Map.entry(Category.Embroidery, "Hafciarstwo"),
Map.entry(Category.Knitting, "Dzierganie"),
Map.entry(Category.Carpentry, "Ciesielstwo"),
Map.entry(Category.Other, "Inne")
);
public enum Status {
ACTIVE, INACTIVE
}

View File

@@ -2,14 +2,18 @@ spring.application.name=ArtisanConnectBackend
## PostgreSQL
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=postgres
spring.datasource.password=postgres
#initial data for db injection
spring.sql.init.data-locations=classpath:sql/data.sql
spring.sql.init.mode=always
spring.jpa.defer-datasource-initialization=true
# create and drop table, good for testing, production set to none or comment it
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.ddl-auto=create-drop
spring.web.resources.static-locations=classpath:/static/,file:images/
file.upload-dir=/Users/andsol/Desktop/uploads
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

View File

@@ -0,0 +1,20 @@
spring.application.name=ArtisanConnectBackend
## PostgreSQL
spring.datasource.url=jdbc:postgresql://db:5432/postgres
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=postgres
spring.datasource.password=postgres
#initial data for db injection
spring.sql.init.data-locations=classpath:sql/data.sql
spring.sql.init.mode=always
spring.jpa.defer-datasource-initialization=true
# create and drop table, good for testing, production set to none or comment it
spring.jpa.hibernate.ddl-auto=update
#file.upload-dir=/Users/andsol/Desktop/uploads
file.upload-dir=/app/images
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

View File

@@ -0,0 +1,122 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/26.2.2 Chrome/134.0.6998.178 Electron/35.1.2 Safari/537.36" version="26.2.2">
<diagram name="Strona-1" id="y3JY7GFactLq4jGyxg2V">
<mxGraphModel dx="2180" dy="884" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="Ht36qwYIb0A0iwGBjog0-3" value="&lt;font style=&quot;font-size: 15px;&quot;&gt;&lt;b&gt;Backend&lt;/b&gt;&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;fillColor=#f5f5f5;fillStyle=auto;strokeColor=#666666;shadow=0;glass=0;fontColor=#333333;" vertex="1" parent="1">
<mxGeometry x="270" y="520" width="534" height="270" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-4" value="&lt;font style=&quot;font-size: 15px;&quot;&gt;&lt;b&gt;Backend&lt;/b&gt;&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;fillColor=#f5f5f5;fillStyle=auto;strokeColor=#666666;shadow=0;glass=0;fontColor=#333333;" parent="1" vertex="1">
<mxGeometry x="324" y="100" width="470" height="230" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="HPrCYg6BNmEDSauX9FRR-2" target="HPrCYg6BNmEDSauX9FRR-7" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-2" value="Postman" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="34" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="HPrCYg6BNmEDSauX9FRR-7" target="HPrCYg6BNmEDSauX9FRR-10" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-7" value="Controller" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="364" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="HPrCYg6BNmEDSauX9FRR-10" target="HPrCYg6BNmEDSauX9FRR-11" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-10" value="Service" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="554" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-11" value="Repository" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="664" y="130" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-17" value="DTO" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="504" y="270" width="30" height="20" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-19" value="Entity" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="704" y="230" width="40" height="20" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-1" value="DTO" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="224" y="270" width="40" height="20" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-44" value="Przesyłanie danych na serwer" style="text;strokeColor=none;fillColor=none;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;" parent="1" vertex="1">
<mxGeometry x="226" y="40" width="376" height="40" as="geometry" />
</mxCell>
<mxCell id="HPrCYg6BNmEDSauX9FRR-50" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#DF8C42;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;shape=mxgraph.veeam2.restful_api;" parent="1" vertex="1">
<mxGeometry x="10" y="10" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-1" value="Przesyłanie zdjęć na backend" style="text;strokeColor=none;fillColor=none;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;" vertex="1" parent="1">
<mxGeometry x="194" y="460" width="440" height="40" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="Ht36qwYIb0A0iwGBjog0-2" target="Ht36qwYIb0A0iwGBjog0-4">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-2" value="Postman" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-20" y="625" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="Ht36qwYIb0A0iwGBjog0-4" target="Ht36qwYIb0A0iwGBjog0-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-4" value="ImageController&lt;div&gt;(Validation)&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="307" y="625" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-5" value="ImageService" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;align=center;" vertex="1" parent="1">
<mxGeometry x="550" y="580" width="196" height="150" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-9" value="MultipartFile + PathVariable" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="140" y="640" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-17" value="saveImageToStorage()" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="580" y="620" width="136" height="30" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-18" value="addImageNameToDB()" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="582" y="680" width="132" height="30" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-20" value="Pobieranie zdjęć z backendu" style="text;strokeColor=none;fillColor=none;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;" vertex="1" parent="1">
<mxGeometry x="194" y="1010" width="440" height="40" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-22" value="&lt;font style=&quot;font-size: 15px;&quot;&gt;&lt;b&gt;Backend&lt;/b&gt;&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;fillColor=#f5f5f5;fillStyle=auto;strokeColor=#666666;shadow=0;glass=0;fontColor=#333333;" vertex="1" parent="1">
<mxGeometry x="260" y="1079" width="534" height="290" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="Ht36qwYIb0A0iwGBjog0-23" target="Ht36qwYIb0A0iwGBjog0-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-30" value="GetRequest" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Ht36qwYIb0A0iwGBjog0-29">
<mxGeometry x="0.2401" relative="1" as="geometry">
<mxPoint x="-19" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-23" value="Postman" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-110" y="1197" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-24" value="/api/v1/images/get/{filename}" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-135" y="1160" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.25;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;" edge="1" parent="1" source="Ht36qwYIb0A0iwGBjog0-26" target="Ht36qwYIb0A0iwGBjog0-23">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-36" value="MultipartFile(content-type image/jpeg)" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Ht36qwYIb0A0iwGBjog0-35">
<mxGeometry x="0.0779" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-26" value="ImageController" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="294" y="1197" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-37" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="Ht36qwYIb0A0iwGBjog0-31" target="Ht36qwYIb0A0iwGBjog0-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-31" value="ImageService" style="rounded=0;whiteSpace=wrap;html=1;verticalAlign=top;align=center;" vertex="1" parent="1">
<mxGeometry x="534" y="1152" width="196" height="150" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-32" value="getImage()" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="564" y="1209" width="136" height="30" as="geometry" />
</mxCell>
<mxCell id="Ht36qwYIb0A0iwGBjog0-38" value="/api/v1/images/upload/{noticeID}" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-60" y="590" width="200" height="30" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>