42 Commits

Author SHA1 Message Date
Patryk
422daeb99e Merge remote-tracking branch 'origin/fix-payments' into fix-payments 2025-06-09 20:58:40 +02:00
Patryk
3204b921c4 fix tapy 2025-06-09 20:58:09 +02:00
f56ffacec3 filter 2025-06-09 20:39:57 +02:00
Patryk
1ec6e62c04 fix payments and add new functions 2025-06-08 19:54:15 +02:00
00b9f99af5 Refactor
added possibility to get attributes
2025-06-08 14:36:38 +02:00
81cbc1f4b2 ClientID for orders 2025-06-08 10:25:36 +02:00
190083c133 ClientId from token in Wishlist controller 2025-06-07 15:18:51 +02:00
501121f235 Merge branch 'autoryzacja' 2025-06-07 14:38:50 +02:00
62a5ad1bc6 MailSender is working 2025-06-07 13:20:37 +02:00
5262749e2d autoryzacja google 2025-06-06 16:05:18 +02:00
5f548de73a dto change to show good role 2025-06-04 12:07:56 +02:00
ffbd8d220c dużo lepsza autoryzacja teraz, dużo lepsza. Tokeny wygasają, można mieć tylko jeden aktywny token per user 2025-06-02 13:46:09 +02:00
0d32b4a495 Merge branch 'paymentIntegration' into autoryzacja
# Conflicts:
#	src/main/resources/application.properties
2025-06-01 09:51:53 +02:00
8ea5d84779 change trasacitonID 2025-05-31 12:47:03 +02:00
a09603f8cb add toString to TransactionPaymentResponseDTO 2025-05-31 12:45:01 +02:00
45c607060a test payment request v2 2025-05-31 12:38:54 +02:00
0b85fed4b8 test payment request 2025-05-31 12:33:38 +02:00
d2163e1601 Merge remote-tracking branch 'origin/paymentIntegration' 2025-05-31 12:12:03 +02:00
293be1d46e Autoryzacja początkowo zaimplementowana 2025-05-31 12:02:15 +02:00
f4c8177270 change wishlist toogle 2025-05-29 23:17:54 +02:00
Patryk
9b64dc8da8 fix return url 2025-05-26 21:59:26 +02:00
Patryk
12cb37127b ad: handling of payment notifications 2025-05-26 21:51:24 +02:00
Patryk
281cc627de add payment notification endpoint 2025-05-26 20:36:39 +02:00
Patryk
6363f966f6 Integrate simple payment handling with WebClient and persist results 2025-05-23 19:06:19 +02:00
Patryk
c642f6f87b create order and change order status 2025-05-20 22:25:00 +02:00
Patryk
65524d0f25 Merge branch 'main' into initOrderAndPayments 2025-05-20 21:25:10 +02:00
Patryk
71fdf1640a change order entities and ad tpay api keys 2025-05-20 21:17:31 +02:00
8fae9f1e55 fix of Kiedy usuwasz ogłoszenie, nie usuwają zdjęcia z bazy i z dysku 2025-05-20 10:37:04 +02:00
d869a18901 boost controller endpoint little fix
+ get rid of not used dependencies
2025-05-16 13:42:24 +02:00
Patryk
1d55f40753 add boostNotice function 2025-05-15 20:29:37 +02:00
3d205df038 few cosmetic fixes 2025-05-14 08:35:33 +02:00
Patryk
5ccfc6ba2c change repository to service in wishlist, add @Lazy 2025-05-13 20:54:44 +02:00
Patryk
f0e3a129d0 change repository to service in wishlist, add @Lazy 2025-05-13 20:54:39 +02:00
Patryk
cdd31fd6b7 init wishlist files 2025-05-12 19:57:00 +02:00
09c15e70d9 New way of categories transfer 2025-05-05 10:10:03 +02:00
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
74 changed files with 2422 additions and 358 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

65
pom.xml
View File

@@ -10,7 +10,7 @@
</parent>
<groupId>_11.asktpk</groupId>
<artifactId>ArtisanConnectBackend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>1.0.0</version>
<name>ArtisanConnectBackend</name>
<description>ArtisanConnectBackend</description>
<url/>
@@ -34,31 +34,21 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-oauth2-client</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-docker-compose</artifactId>-->
<!-- <scope>runtime</scope>-->
<!-- <optional>true</optional>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
<version>2.4.12</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
@@ -73,15 +63,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-test</artifactId>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
@@ -93,6 +78,42 @@
<artifactId>jakarta.validation-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</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,15 @@
package _11.asktpk.artisanconnectbackend.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class AppConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

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,38 @@
package _11.asktpk.artisanconnectbackend.config;
import _11.asktpk.artisanconnectbackend.security.JwtRequestFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtRequestFilter jwtRequestFilter;
public SecurityConfig(JwtRequestFilter jwtRequestFilter) {
this.jwtRequestFilter = jwtRequestFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configure(http))
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**", "/api/v1/payments/notification").permitAll()
.anyRequest().authenticated())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}

View File

@@ -0,0 +1,126 @@
package _11.asktpk.artisanconnectbackend.controller;
import _11.asktpk.artisanconnectbackend.dto.*;
import _11.asktpk.artisanconnectbackend.entities.Client;
import _11.asktpk.artisanconnectbackend.security.JwtUtil;
import _11.asktpk.artisanconnectbackend.service.ClientService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {
private final ClientService clientService;
private final JwtUtil jwtUtil;
public AuthController(ClientService clientService, JwtUtil jwtUtil) {
this.clientService = clientService;
this.jwtUtil = jwtUtil;
}
@PostMapping("/login")
public ResponseEntity<AuthResponseDTO> login(@RequestBody AuthRequestDTO authRequestDTO) {
if (clientService.checkClientCredentials(authRequestDTO)) {
Client client = clientService.getClientByEmail(authRequestDTO.getEmail());
Long userId = client.getId();
String userRole = client.getRole().getRole();
String token = jwtUtil.generateToken(client.getEmail(), userRole, userId);
log.info("User logged in with {}", client.getEmail());
return ResponseEntity.status(HttpStatus.OK)
.body(new AuthResponseDTO(userId, userRole, token));
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
}
}
@PostMapping("/register")
public ResponseEntity<AuthResponseDTO> register(@RequestBody ClientRegistrationDTO clientDTO) {
if (clientService.getClientByEmail(clientDTO.getEmail()) != null) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
ClientDTO savedClient = clientService.registerClient(clientDTO);
String token = jwtUtil.generateToken(
savedClient.getEmail(),
savedClient.getRole(),
savedClient.getId()
);
log.info("New user registered with {}", savedClient.getEmail());
return ResponseEntity.status(HttpStatus.CREATED)
.body(new AuthResponseDTO(
savedClient.getId(),
savedClient.getRole(),
token
));
}
@PostMapping("/logout")
public ResponseEntity<RequestResponseDTO> logout(HttpServletRequest request) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
jwtUtil.blacklistToken(token);
return ResponseEntity.ok(new RequestResponseDTO("Successfully logged out"));
}
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new RequestResponseDTO("Invalid token"));
}
@PostMapping("/google")
public ResponseEntity<?> authenticateWithGoogle(@RequestBody GoogleAuthRequestDTO dto) {
try {
String accessToken = dto.getGoogleToken();
String googleUserInfoUrl = "https://www.googleapis.com/oauth2/v3/userinfo";
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
HttpEntity<String> entity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Map> response = restTemplate.exchange(
googleUserInfoUrl, HttpMethod.GET, entity, Map.class);
Map<String, Object> userInfo = response.getBody();
// String googleId = (String) userInfo.get("sub"); Potencjalnie możemy używać googlowskiego ID, ale to ma konflikt z naszym generowanym
assert userInfo != null;
String email = (String) userInfo.get("email");
String name = (String) userInfo.get("name");
Client client = clientService.getClientByEmail(email);
if (client == null) {
client = new Client();
client.setEmail(email);
client.setFirstName(name);
client.setRole(clientService.getUserRole()); // to pobiera po prostu role "USER" z tabeli w bazie
clientService.saveClientToDB(client);
}
String jwt = jwtUtil.generateToken(client.getEmail(), client.getRole().getRole(), client.getId());
log.info("User authenticated with google: {}", email);
return ResponseEntity.ok(new AuthResponseDTO(client.getId(), client.getRole().getRole(), jwt));
} catch (HttpClientErrorException httpClientErrorException) {
log.error("Token is invalid or expired");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new RequestResponseDTO("Invalid access token"));
} catch (Exception e) {
log.error("Error while checking Google access token", e);
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(new RequestResponseDTO("Authentication Error (Google): " + e.getMessage()));
}
}
}

View File

@@ -24,16 +24,16 @@ public class ClientController {
}
@GetMapping("/get/{id}")
public ResponseEntity getClientById(@PathVariable long id) {
public ResponseEntity<?> getClientById(@PathVariable long id) {
if(clientService.getClientById(id) != null) {
return new ResponseEntity(clientService.getClientById(id), HttpStatus.OK);
return new ResponseEntity<>(clientService.getClientByIdDTO(id), HttpStatus.OK);
} else {
return new ResponseEntity(HttpStatus.NOT_FOUND);
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@PostMapping("/add")
public ResponseEntity addClient(@RequestBody ClientDTO clientDTO) {
public ResponseEntity<?> addClient(@RequestBody ClientDTO clientDTO) {
if(clientService.clientExists(clientDTO.getId())) {
return new ResponseEntity<>(HttpStatus.CONFLICT);
} else {
@@ -43,7 +43,7 @@ public class ClientController {
// TODO: do zrobienia walidacja danych
@PutMapping("/edit/{id}")
public ResponseEntity updateClient(@PathVariable("id") long id, @RequestBody ClientDTO clientDTO) {
public ResponseEntity<?> updateClient(@PathVariable("id") long id, @RequestBody ClientDTO clientDTO) {
if(clientService.clientExists(id)) {
return new ResponseEntity<>(clientService.updateClient(id, clientDTO),HttpStatus.OK);
} else {
@@ -52,7 +52,7 @@ public class ClientController {
}
@DeleteMapping("/delete/{id}")
public ResponseEntity deleteClient(@PathVariable("id") long id) {
public ResponseEntity<?> deleteClient(@PathVariable("id") long id) {
if(clientService.clientExists(id)) {
clientService.deleteClient(id);
return new ResponseEntity<>(HttpStatus.OK);

View File

@@ -0,0 +1,25 @@
package _11.asktpk.artisanconnectbackend.controller;
import _11.asktpk.artisanconnectbackend.dto.EmailDTO;
import _11.asktpk.artisanconnectbackend.service.EmailService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/email")
public class EmailController {
private final EmailService emailService;
public EmailController(EmailService emailService) {
this.emailService = emailService;
}
@PostMapping("/send")
public ResponseEntity<String> sendEmail(@RequestBody EmailDTO email) {
try {
emailService.sendEmail(email);
return ResponseEntity.ok("Email wysłany pomyślnie");
} catch (Exception e) {
return ResponseEntity.status(500).body("Błąd podczas wysyłania emaila");
}
}
}

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 jakarta.persistence.EntityNotFoundException;
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<?> getImagesNamesList(@PathVariable("id") Long noticeId) {
List<String> result;
try {
noticeService.getNoticeById(noticeId);
result = imageService.getImagesList(noticeId);
return ResponseEntity.ok(result);
} catch (EntityNotFoundException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new RequestResponseDTO(e.getMessage()));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new RequestResponseDTO(e.getMessage()));
}
}
@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,18 +1,17 @@
package _11.asktpk.artisanconnectbackend.controller;
import _11.asktpk.artisanconnectbackend.dto.*;
import _11.asktpk.artisanconnectbackend.service.ClientService;
import _11.asktpk.artisanconnectbackend.service.NoticeService;
import _11.asktpk.artisanconnectbackend.dto.NoticeDTO;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import _11.asktpk.artisanconnectbackend.utils.Tools;
import jakarta.persistence.EntityNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
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.Arrays;
import java.util.List;
@RequestMapping("/api/v1/notices")
@@ -20,19 +19,21 @@ import java.util.List;
public class NoticeController {
private final NoticeService noticeService;
private final ClientService clientService;
private final Tools tools;
public NoticeController(NoticeService noticeService, ClientService clientService) {
public NoticeController(NoticeService noticeService, ClientService clientService, Tools tools) {
this.noticeService = noticeService;
this.clientService = clientService;
this.tools = tools;
}
@GetMapping("/get/all")
public List<NoticeDTO> getAllNotices() {
public List<NoticeResponseDTO> getAllNotices() {
return noticeService.getAllNotices();
}
@GetMapping("/get/{id}")
public ResponseEntity getNoticeById(@PathVariable long id) {
public ResponseEntity<?> getNoticeById(@PathVariable long id) {
if (noticeService.noticeExists(id)) {
return ResponseEntity.ok(noticeService.getNoticeById(id));
} else {
@@ -41,55 +42,34 @@ public class NoticeController {
}
@PostMapping("/add")
public ResponseEntity<String> addNotice(@RequestBody NoticeDTO dto) {
if (!clientService.clientExists(dto.getClientId())) {
public ResponseEntity<NoticeAdditionDTO> addNotice(@RequestBody NoticeRequestDTO dto, HttpServletRequest request) {
Long clientId = tools.getClientIdFromRequest(request);
if (!clientService.clientExists(clientId)) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body("Nie znaleziono klienta o ID: " + dto.getClientId());
.body(new NoticeAdditionDTO("Nie znaleziono klienta o ID: " + clientId));
}
dto.setPublishDate(java.time.LocalDateTime.now());
dto.setClientId(clientId);
noticeService.addNotice(dto);
return ResponseEntity.status(HttpStatus.CREATED).body("Dodano ogłoszenie.");
}
// TODO: zamiast dodawać tutaj pętlą, musi to robić NoticeService, trzeba zaimplementować odpowienią metodę
@PostMapping("/bulk_add")
public ResponseEntity<String> addNotices(@RequestBody List<NoticeDTO> notices_list) {
ResponseEntity<String> response = new ResponseEntity<>(HttpStatus.CREATED);
List<String> errors = new ArrayList<>();
boolean isError = false;
if (notices_list.isEmpty()) {
return response.status(HttpStatus.BAD_REQUEST).body("Lista ogłoszeń jest pusta.");
if (dto.getCategory() == null || !Arrays.asList(Enums.Category.values()).contains(dto.getCategory())) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new NoticeAdditionDTO("Nie ma takiej kategorii"));
}
for (NoticeDTO dto : notices_list) {
if (!clientService.clientExists(dto.getClientId())) {
isError = true;
errors.add(dto.getClientId().toString());
} else {
if (!isError) {
noticeService.addNotice(dto);
}
}
}
Long newNoticeId = noticeService.addNotice(dto);
if (isError) {
return response.status(HttpStatus.BAD_REQUEST).body("Nie znaleziono klientów: " + errors);
}
return response;
return ResponseEntity.status(HttpStatus.CREATED).body(new NoticeAdditionDTO(newNoticeId ,"Dodano ogłoszenie."));
}
@PutMapping("/edit/{id}")
public ResponseEntity<Object> editNotice(@PathVariable("id") long id, @RequestBody NoticeDTO dto) {
public ResponseEntity<Object> editNotice(@PathVariable("id") long id, @RequestBody NoticeRequestDTO dto, HttpServletRequest request) {
Long clientIdFromToken = tools.getClientIdFromRequest(request);
if (noticeService.noticeExists(id)) {
if (!noticeService.isNoticeOwnedByClient(id, clientIdFromToken)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new RequestResponseDTO("Nie masz uprawnień do edycji tego ogłoszenia."));
}
try {
return new ResponseEntity<>(noticeService.updateNotice(id, dto), HttpStatus.OK);
return ResponseEntity.status(HttpStatus.OK).body(noticeService.updateNotice(id, dto));
} catch (EntityNotFoundException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
@@ -99,61 +79,28 @@ public class NoticeController {
}
@DeleteMapping("/delete/{id}")
public ResponseEntity deleteNotice(@PathVariable("id") long id) {
public ResponseEntity<RequestResponseDTO> deleteNotice(@PathVariable("id") long id, HttpServletRequest request) {
Long clientIdFromToken = tools.getClientIdFromRequest(request);
if (noticeService.noticeExists(id)) {
if (!noticeService.isNoticeOwnedByClient(id, clientIdFromToken)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new RequestResponseDTO("Nie masz uprawnień do usunięcia tego ogłoszenia."));
}
noticeService.deleteNotice(id);
return new ResponseEntity<>(HttpStatus.OK);
return ResponseEntity.status(HttpStatus.OK).body(new RequestResponseDTO("Pomyślnie usunięto ogłoszenie o ID: " + id));
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new RequestResponseDTO("Nie znaleziono ogłoszenia o ID: " + id));
}
}
@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);
@PostMapping("/boost")
public ResponseEntity<RequestResponseDTO> boostNotice(@RequestBody NoticeBoostDTO dto, HttpServletRequest request) {
Long clientId = tools.getClientIdFromRequest(request);
if (noticeService.isNoticeOwnedByClient(dto.getNoticeId(), clientId)) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new RequestResponseDTO("Ogłoszenie nie istnieje lub nie należy do zalogowanego klienta."));
}
noticeService.boostNotice(dto.getNoticeId());
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());
}
return ResponseEntity.status(HttpStatus.OK).body(new RequestResponseDTO("Ogłoszenie zostało pomyślnie wypromowane."));
}
@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,129 @@
package _11.asktpk.artisanconnectbackend.controller;
import _11.asktpk.artisanconnectbackend.dto.*;
import _11.asktpk.artisanconnectbackend.entities.Client;
import _11.asktpk.artisanconnectbackend.entities.Order;
import _11.asktpk.artisanconnectbackend.entities.Payment;
import _11.asktpk.artisanconnectbackend.service.OrderService;
import _11.asktpk.artisanconnectbackend.service.PaymentService;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import _11.asktpk.artisanconnectbackend.utils.Tools;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {
private final OrderService orderService;
private final PaymentService paymentService;
private final Tools tools;
public OrderController(OrderService orderService, PaymentService paymentService, Tools tools) {
this.orderService = orderService;
this.paymentService = paymentService;
this.tools = tools;
}
@PostMapping("/add")
public ResponseEntity<?> addClient(@RequestBody OrderDTO orderDTO, HttpServletRequest request) {
orderDTO.setClientId(tools.getClientIdFromRequest(request));
return ResponseEntity.status(HttpStatus.CREATED).body(orderService.addOrder(orderDTO));
}
@PutMapping("/changeStatus")
public ResponseEntity<?> changeStatus(@RequestBody OrderStatusDTO orderStatusDTO) {
return ResponseEntity.status(HttpStatus.OK).body(orderService.changeOrderStatus(orderStatusDTO.getId(), orderStatusDTO.getStatus()));
}
@PostMapping("/token")
public ResponseEntity<?> fetchToken(HttpServletRequest request,@RequestParam Long orderId) {
Order order = orderService.getOrderById(orderId);
Client client = order.getClient();
OAuthPaymentResponseDTO authPaymentDTO = paymentService.getOAuthToken();
TransactionPaymentRequestDTO.Payer payer = new TransactionPaymentRequestDTO.Payer(
client.getEmail(), client.getFirstName()+' '+client.getLastName());
String paymentDescription = order.getOrderType() == Enums.OrderType.ACTIVATION ? "Aktywacja ogłoszenia" : "Podbicie ogłoszenia";
paymentDescription += order.getNotice().getTitle();
TransactionPaymentRequestDTO paymentRequest = new TransactionPaymentRequestDTO(
order.getAmount(), paymentDescription, payer);
String response = paymentService.createTransaction(order, authPaymentDTO.getAccess_token(), paymentRequest);
return ResponseEntity.status(HttpStatus.OK).body(response);
}
@GetMapping("/get/all")
public ResponseEntity<List<OrderWithPaymentsDTO>> getOrders(HttpServletRequest request) {
Long clientId = tools.getClientIdFromRequest(request);
List<Order> orders = orderService.getOrdersByClientId(clientId);
List<OrderWithPaymentsDTO> dtoList = orders.stream().map(order -> {
OrderWithPaymentsDTO dto = new OrderWithPaymentsDTO();
dto.setOrderId(order.getId());
dto.setOrderType(order.getOrderType().name());
dto.setStatus(order.getStatus().name());
dto.setAmount(order.getAmount());
dto.setCreatedAt(order.getCreatedAt());
List<Payment> payments = paymentService.getPaymentsByOrderId(order.getId());
List<PaymentDTO> paymentDTOs = payments.stream().map(payment -> {
PaymentDTO pDto = new PaymentDTO();
pDto.setPaymentId(payment.getIdPayment());
pDto.setAmount(payment.getAmount());
pDto.setStatus(payment.getStatus().name());
pDto.setTransactionPaymentUrl(payment.getTransactionPaymentUrl());
pDto.setTransactionId(payment.getTransactionId());
return pDto;
}).toList();
dto.setPayments(paymentDTOs);
return dto;
}).toList();
return ResponseEntity.ok(dtoList);
}
@GetMapping("/get/{orderId}")
public ResponseEntity<OrderWithPaymentsDTO> getOrderById(HttpServletRequest request,
@PathVariable Long orderId) {
Long clientId = tools.getClientIdFromRequest(request);
Order order = orderService.getOrderById(orderId);
if (!order.getClient().getId().equals(clientId)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
OrderWithPaymentsDTO dto = new OrderWithPaymentsDTO();
dto.setOrderId(order.getId());
dto.setOrderType(order.getOrderType().name());
dto.setStatus(order.getStatus().name());
dto.setAmount(order.getAmount());
dto.setCreatedAt(order.getCreatedAt());
List<Payment> payments = paymentService.getPaymentsByOrderId(order.getId());
List<PaymentDTO> paymentDTOs = payments.stream().map(payment -> {
PaymentDTO pDto = new PaymentDTO();
pDto.setPaymentId(payment.getIdPayment());
pDto.setAmount(payment.getAmount());
pDto.setStatus(payment.getStatus().name());
pDto.setTransactionPaymentUrl(payment.getTransactionPaymentUrl());
pDto.setTransactionId(payment.getTransactionId());
return pDto;
}).toList();
dto.setPayments(paymentDTOs);
return ResponseEntity.ok(dto);
}
}

View File

@@ -0,0 +1,93 @@
package _11.asktpk.artisanconnectbackend.controller;
import _11.asktpk.artisanconnectbackend.entities.Notice;
import _11.asktpk.artisanconnectbackend.entities.Order;
import _11.asktpk.artisanconnectbackend.entities.Payment;
import _11.asktpk.artisanconnectbackend.repository.PaymentRepository;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import org.springframework.beans.factory.annotation.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/v1/payments")
public class PaymentController {
@Value("${tpay.securityCode}")
private String sellerSecurityCode;
private static final Logger log = LoggerFactory.getLogger(PaymentController.class);
private final PaymentRepository paymentRepository;
public PaymentController(PaymentRepository paymentRepository) {
this.paymentRepository = paymentRepository;
}
@PostMapping(value = "/notification", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<String> handleTpayNotification(@RequestParam Map<String, String> params) {
String id = params.get("id");
String trId = params.get("tr_id");
String trAmount = params.get("tr_amount");
String trCrc = params.get("tr_crc");
String md5sum = params.get("md5sum");
String trStatus = params.get("tr_status");
String expectedMd5 = DigestUtils.md5DigestAsHex(
(id + trId + trAmount + trCrc + sellerSecurityCode).getBytes()
);
if (!expectedMd5.equals(md5sum)) {
return ResponseEntity.status(400).body("INVALID CHECKSUM");
}
Optional<Payment> optionalPayment = paymentRepository.findByTransactionId(trId);
if (optionalPayment.isPresent()) {
Payment payment = optionalPayment.get();
if ("true".equalsIgnoreCase(trStatus) || "PAID".equalsIgnoreCase(trStatus)) {
payment.setStatus(Enums.PaymentStatus.CORRECT);
if (payment.getOrder() != null) {
Order order = payment.getOrder();
order.setStatus(Enums.OrderStatus.COMPLETED);
Notice notice = order.getNotice();
if (order.getOrderType() == Enums.OrderType.ACTIVATION) {
notice.setStatus(Enums.Status.ACTIVE);
} else if (order.getOrderType() == Enums.OrderType.BOOST) {
notice.setPublishDate(LocalDateTime.now());
}
}
} else if ("false".equalsIgnoreCase(trStatus)) {
payment.setStatus(Enums.PaymentStatus.INCORRECT);
if (payment.getOrder() != null) {
payment.getOrder().setStatus(Enums.OrderStatus.CANCELLED);
}
}
paymentRepository.save(payment);
}
return ResponseEntity.ok("TRUE");
}
private String paramsToLogString(Map<String, String> params) {
return params.entrySet().stream()
.map(e -> e.getKey() + " = " + e.getValue())
.collect(Collectors.joining("\n"));
}
}

View File

@@ -0,0 +1,33 @@
package _11.asktpk.artisanconnectbackend.controller;
import _11.asktpk.artisanconnectbackend.dto.CategoriesDTO;
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.ArrayList;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/vars")
public class VariablesController {
@GetMapping("/categories")
public List<CategoriesDTO> getAllVariables() {
List<CategoriesDTO> categoriesDTOList = new ArrayList<>();
for (Map.Entry<Enums.Category, String> entry : Enums.categoryPL.entrySet()) {
CategoriesDTO categoriesDTO = new CategoriesDTO();
categoriesDTO.setLabel(entry.getValue());
categoriesDTO.setValue(entry.getKey().toString());
categoriesDTOList.add(categoriesDTO);
}
return categoriesDTOList;
}
@GetMapping("/statuses")
public List<Enums.Status> getAllStatuses() {
return List.of(Enums.Status.values());
}
}

View File

@@ -0,0 +1,62 @@
package _11.asktpk.artisanconnectbackend.controller;
import _11.asktpk.artisanconnectbackend.dto.NoticeResponseDTO;
import _11.asktpk.artisanconnectbackend.dto.RequestResponseDTO;
import _11.asktpk.artisanconnectbackend.service.ClientService;
import _11.asktpk.artisanconnectbackend.service.NoticeService;
import _11.asktpk.artisanconnectbackend.service.WishlistService;
import _11.asktpk.artisanconnectbackend.utils.Tools;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/v1/wishlist")
public class WishlistController {
private final WishlistService wishlistService;
private final ClientService clientService;
private final NoticeService noticeService;
private final Tools tools;
public WishlistController(WishlistService wishlistService, ClientService clientService, NoticeService noticeService, Tools tools) {
this.wishlistService = wishlistService;
this.clientService = clientService;
this.noticeService = noticeService;
this.tools = tools;
}
@PostMapping("/toggle/{noticeId}")
public ResponseEntity<RequestResponseDTO> toggleWishlist(@PathVariable Long noticeId, HttpServletRequest request) {
Long clientId = tools.getClientIdFromRequest(request);
NoticeResponseDTO noticeResponseDTO = noticeService.getNoticeById(noticeId);
if (noticeResponseDTO == null) {
return ResponseEntity.badRequest().body(new RequestResponseDTO("Notice not found"));
}
boolean added = wishlistService.toggleWishlist(
clientService.getClientById(clientId),
noticeService.getNoticeByIdEntity(noticeId)
);
if (added) {
return ResponseEntity.ok(new RequestResponseDTO("Wishlist entry added"));
} else {
return ResponseEntity.ok(new RequestResponseDTO("Wishlist entry removed"));
}
}
// @GetMapping("/{clientId}")
// public ResponseEntity<List<WishlistDTO>> getWishlist(@PathVariable Long clientId) {
// List<WishlistDTO> wishlist = wishlistService.getWishlistForClientId(clientId);
// return ResponseEntity.ok(wishlist);
// }
@GetMapping("/")
public List<NoticeResponseDTO> getWishlistForClient(HttpServletRequest request) {
Long clientId = tools.getClientIdFromRequest(request);
return wishlistService.getNoticesInWishlist(clientId);
}
}

View File

@@ -0,0 +1,11 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class AttributeDto {
private String name;
private String value;
}

View File

@@ -0,0 +1,10 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class AuthRequestDTO {
private String email;
private String password;
}

View File

@@ -0,0 +1,12 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter @AllArgsConstructor
public class AuthResponseDTO {
private Long user_id;
private String user_role;
private String token;
}

View File

@@ -0,0 +1,16 @@
package _11.asktpk.artisanconnectbackend.dto;
//[
// { "label": "Meble", "value": "Furniture" },
// { "label": "Biżuteria", "value": "Jewelry" },
// { "label": "Ceramika", "value": "Ceramics" }
//]
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class CategoriesDTO {
String label;
String value;
}

View File

@@ -6,8 +6,6 @@ import lombok.Setter;
import jakarta.validation.constraints.Email;
import _11.asktpk.artisanconnectbackend.utils.Enums.Role;
@Getter @Setter
public class ClientDTO {
private Long id;
@@ -18,5 +16,5 @@ public class ClientDTO {
private String firstName;
private String lastName;
private String image;
private Role role;
private String role;
}

View File

@@ -0,0 +1,16 @@
package _11.asktpk.artisanconnectbackend.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class ClientRegistrationDTO {
@Email
@NotBlank
private String email;
private String firstName;
private String lastName;
private String password;
}

View File

@@ -0,0 +1,20 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.Getter;
import lombok.Setter;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
@Getter
@Setter
public class EmailDTO {
@Email(message = "Podaj poprawny adres email")
@NotBlank(message = "Adres email nie może być pusty")
private String to;
@NotBlank(message = "Temat nie może być pusty")
private String subject;
@NotBlank(message = "Treść nie może być pusta")
private String body;
}

View File

@@ -0,0 +1,9 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class GoogleAuthRequestDTO {
private String googleToken;
}

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

@@ -0,0 +1,9 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class NoticeBoostDTO {
private Long noticeId;
}

View File

@@ -1,51 +0,0 @@
package _11.asktpk.artisanconnectbackend.dto;
import _11.asktpk.artisanconnectbackend.entities.AttributesNotice;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.List;
@Getter @Setter
public class NoticeDTO {
private long noticeId;
private String title;
private Long clientId;
private String description;
private Double price;
private Enums.Category category;
private List<String> images;
private Enums.Status status;
private LocalDateTime publishDate;
private List<AttributesNotice> attributesNotices;
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,24 @@
package _11.asktpk.artisanconnectbackend.dto;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class NoticeRequestDTO {
private String title;
private Long clientId;
private String description;
private Double price;
private Enums.Category category;
private Enums.Status status;
public NoticeRequestDTO() {
}
}

View File

@@ -0,0 +1,33 @@
package _11.asktpk.artisanconnectbackend.dto;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.List;
@Getter @Setter
public class NoticeResponseDTO {
private long noticeId;
private String title;
private Long clientId;
private String description;
private Double price;
private Enums.Category category;
private Enums.Status status;
private LocalDateTime publishDate;
private List<AttributeDto> attributes;
public NoticeResponseDTO() {
}
}

View File

@@ -0,0 +1,15 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class OAuthPaymentResponseDTO {
private long issued_at;
private String scope;
private String token_type;
private int expires_in;
private String client_id;
private String access_token;
}

View File

@@ -0,0 +1,13 @@
package _11.asktpk.artisanconnectbackend.dto;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class OrderDTO {
private Long clientId;
private Long noticeId;
private Enums.OrderType orderType;
}

View File

@@ -0,0 +1,12 @@
package _11.asktpk.artisanconnectbackend.dto;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class OrderStatusDTO {
public long id;
public Enums.OrderStatus status;
}

View File

@@ -0,0 +1,62 @@
package _11.asktpk.artisanconnectbackend.dto;
import java.time.LocalDateTime;
import java.util.List;
public class OrderWithPaymentsDTO {
private Long orderId;
private String orderType;
private String status;
private Double amount;
private LocalDateTime createdAt;
private List<PaymentDTO> payments;
// Gettery i settery
public Long getOrderId() {
return orderId;
}
public void setOrderId(Long orderId) {
this.orderId = orderId;
}
public String getOrderType() {
return orderType;
}
public void setOrderType(String orderType) {
this.orderType = orderType;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public List<PaymentDTO> getPayments() {
return payments;
}
public void setPayments(List<PaymentDTO> payments) {
this.payments = payments;
}
}

View File

@@ -0,0 +1,34 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PaymentDTO {
private Long paymentId;
private Double amount;
private String status;
private String transactionPaymentUrl;
private String transactionId;
public void setPaymentId(Long paymentId) {
this.paymentId = paymentId;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public void setStatus(String status) {
this.status = status;
}
public void setTransactionPaymentUrl(String transactionPaymentUrl) {
this.transactionPaymentUrl = transactionPaymentUrl;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
}

View File

@@ -0,0 +1,17 @@
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;
}
public String toJSON() {
return "{\"message\":\"" + message + "\"}";
}
}

View File

@@ -0,0 +1,23 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class TransactionPaymentRequestDTO {
private double amount;
private String description;
private Payer payer;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Payer {
private String email;
private String name;
}
}

View File

@@ -0,0 +1,80 @@
package _11.asktpk.artisanconnectbackend.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransactionPaymentResponseDTO {
private String result;
private String requestId;
private String transactionId;
private String title;
private String posId;
private String status;
private DateInfo date;
private double amount;
private String currency;
private String description;
private String hiddenDescription;
private Payer payer;
private Payments payments;
private String transactionPaymentUrl;
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class DateInfo {
private String creation;
private String realization;
}
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Payer {
private String payerId;
private String email;
private String name;
private String phone;
private String address;
private String city;
private String country;
private String postalCode;
}
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Payments {
private String status;
private String method;
private double amountPaid;
private DateInfo date;
}
@Override
public String toString() {
return "YourClassName{" +
"result='" + result + '\'' +
", requestId='" + requestId + '\'' +
", transactionId='" + transactionId + '\'' +
", title='" + title + '\'' +
", posId='" + posId + '\'' +
", status='" + status + '\'' +
", date=" + date +
", amount=" + amount +
", currency='" + currency + '\'' +
", description='" + description + '\'' +
", hiddenDescription='" + hiddenDescription + '\'' +
", payer=" + payer +
", payments=" + payments +
", transactionPaymentUrl='" + transactionPaymentUrl + '\'' +
'}';
}
}

View File

@@ -0,0 +1,12 @@
package _11.asktpk.artisanconnectbackend.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class WishlistDTO {
private Long id;
private Long clientId;
private Long noticeId;
}

View File

@@ -1,9 +1,12 @@
package _11.asktpk.artisanconnectbackend.entities;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "attribute_values")
@Getter @Setter
public class AttributeValues {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -14,6 +17,4 @@ public class AttributeValues {
private Attributes attribute;
private String value;
// Getters, setters, and constructors
}

View File

@@ -1,10 +1,14 @@
package _11.asktpk.artisanconnectbackend.entities;
import jakarta.persistence.*;
import lombok.Setter;
import lombok.Getter;
import java.util.List;
@Entity
@Table(name = "attributes")
@Getter @Setter
public class Attributes {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -12,8 +16,6 @@ public class Attributes {
private String name;
@OneToMany(mappedBy = "attribute", cascade = CascadeType.ALL)
@OneToMany(mappedBy = "attribute")
private List<AttributeValues> attributeValues;
// Getters, setters, and constructors
}

View File

@@ -1,21 +1,20 @@
package _11.asktpk.artisanconnectbackend.entities;
import jakarta.persistence.*;
import lombok.Setter;
import lombok.Getter;
@Entity
@Table(name = "attributes_notice")
@Getter @Setter
public class AttributesNotice {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "id_notice")
private Notice notice;
private Long notice_id;
@ManyToOne
@JoinColumn(name = "id_value")
private AttributeValues attributeValue;
// Getters, setters, and constructors
}

View File

@@ -1,11 +1,11 @@
package _11.asktpk.artisanconnectbackend.entities;
import _11.asktpk.artisanconnectbackend.utils.Enums.Role;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import java.util.Date;
import java.util.List;
@Entity
@@ -24,14 +24,15 @@ public class Client {
private String lastName;
private String image; // Optional field
private String image;
@Enumerated(EnumType.STRING)
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "role_id", referencedColumnName = "id")
private Role role;
// @OneToMany(mappedBy = "client", cascade = CascadeType.ALL)
// private List<Notice> notices;
@OneToMany(mappedBy = "client", cascade = CascadeType.ALL)
private List<Orders> orders;
private List<Order> orders;
@CreationTimestamp
private Date createdAt;
}

View File

@@ -1,16 +0,0 @@
package _11.asktpk.artisanconnectbackend.entities;
import jakarta.persistence.*;
@Entity
@Table(name = "global_variables")
public class GlobalVariables {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String value;
// Getters, setters, and constructors
}

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,20 +30,17 @@ public class Notice {
@Enumerated(EnumType.STRING)
private Category category;
@ElementCollection
private List<String> images;
@Enumerated(EnumType.STRING)
private Status status;
private LocalDateTime publishDate;
@OneToMany(mappedBy = "notice", cascade = CascadeType.ALL)
@OneToMany(mappedBy = "notice_id")
private List<AttributesNotice> attributesNotices;
@OneToMany(mappedBy = "notice", cascade = CascadeType.ALL)
private List<Orders> orders;
@OneToMany(mappedBy = "notice")
private List<Order> orders;
@OneToMany(mappedBy = "notice", cascade = CascadeType.ALL)
private List<Payments> payments;
// @OneToMany(mappedBy = "notice", cascade = CascadeType.ALL)
// private List<Payment> payment;
}

View File

@@ -0,0 +1,43 @@
package _11.asktpk.artisanconnectbackend.entities;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Entity
@Table(name = "orders")
@Getter
@Setter
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "id_client")
private Client client;
@ManyToOne
@JoinColumn(name = "id_notice")
private Notice notice;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Enums.OrderType orderType;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Enums.OrderStatus status;
@Column(nullable = false)
private Double amount;
@Column(nullable = false)
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,26 +0,0 @@
package _11.asktpk.artisanconnectbackend.entities;
import _11.asktpk.artisanconnectbackend.utils.Enums.Status;
import jakarta.persistence.*;
@Entity
@Table(name = "orders")
public class Orders {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idOrder;
@ManyToOne
@JoinColumn(name = "id_user")
private Client client;
@ManyToOne
@JoinColumn(name = "id_notice")
private Notice notice;
@Enumerated(EnumType.STRING)
private Status status;
// Getters, setters, and constructors
}

View File

@@ -0,0 +1,30 @@
package _11.asktpk.artisanconnectbackend.entities;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "payment")
@Getter @Setter
public class Payment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idPayment;
@ManyToOne
@JoinColumn(name = "id_order")
private Order order;
private Double amount;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Enums.PaymentStatus status;
private String transactionPaymentUrl;
private String transactionId;
}

View File

@@ -1,30 +0,0 @@
package _11.asktpk.artisanconnectbackend.entities;
import _11.asktpk.artisanconnectbackend.utils.Enums.Status;
import jakarta.persistence.*;
@Entity
@Table(name = "payments")
public class Payments {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idPayment;
@ManyToOne
@JoinColumn(name = "id_order")
private Orders order;
@ManyToOne
@JoinColumn(name = "id_notice")
private Notice notice;
private Double noticePublishPrice;
@Enumerated(EnumType.STRING)
private Status status;
private String sessionId;
// Getters, setters, and constructors
}

View File

@@ -0,0 +1,19 @@
package _11.asktpk.artisanconnectbackend.entities;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "roles")
@Getter
@Setter
public class Role {
@Id
private Long id;
@Column(name="rolename")
private String role;
}

View File

@@ -0,0 +1,24 @@
package _11.asktpk.artisanconnectbackend.entities;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "wishlist")
@Getter
@Setter
public class Wishlist {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "client_id", nullable = false)
private Client client;
@ManyToOne
@JoinColumn(name = "notice_id", nullable = false)
private Notice notice;
}

View File

@@ -1,8 +1,8 @@
package _11.asktpk.artisanconnectbackend.repository;
import _11.asktpk.artisanconnectbackend.entities.Client;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ClientRepository extends JpaRepository<Client, Long> {
Client findByEmail(String email);
}

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

@@ -1,8 +1,9 @@
package _11.asktpk.artisanconnectbackend.repository;
import _11.asktpk.artisanconnectbackend.entities.Notice;
import org.springframework.data.jpa.repository.JpaRepository;
public interface NoticeRepository extends JpaRepository<Notice, Long> {
}
boolean existsByIdNoticeAndClientId(long noticeId, long clientId);
}

View File

@@ -0,0 +1,13 @@
package _11.asktpk.artisanconnectbackend.repository;
import _11.asktpk.artisanconnectbackend.entities.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByClientId(Long clientId);
}

View File

@@ -0,0 +1,15 @@
package _11.asktpk.artisanconnectbackend.repository;
import _11.asktpk.artisanconnectbackend.entities.Payment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface PaymentRepository extends JpaRepository<Payment, Long> {
Optional<Payment> findByTransactionId(String transactionId);
List<Payment> findAllByOrderId(Long id);
}

View File

@@ -0,0 +1,12 @@
package _11.asktpk.artisanconnectbackend.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import _11.asktpk.artisanconnectbackend.entities.Role;
@Repository
public interface RolesRepository extends JpaRepository<Role, String> {
Role findRoleById(Long id);
Role findRoleByRole(String role);
}

View File

@@ -0,0 +1,16 @@
package _11.asktpk.artisanconnectbackend.repository;
import _11.asktpk.artisanconnectbackend.entities.Client;
import _11.asktpk.artisanconnectbackend.entities.Notice;
import _11.asktpk.artisanconnectbackend.entities.Wishlist;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface WishlistRepository extends JpaRepository<Wishlist, Long> {
List<Wishlist> findAllByClientId(Long clientId);
Optional<Wishlist> findByClientAndNotice(Client client, Notice notice);
}

View File

@@ -0,0 +1,80 @@
package _11.asktpk.artisanconnectbackend.security;
import _11.asktpk.artisanconnectbackend.dto.RequestResponseDTO;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Collections;
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
public JwtRequestFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String email = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
try {
if (jwtUtil.isBlacklisted(jwt) || !jwtUtil.isLatestToken(jwt)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
String jsonResponse = "{\"error\": \"Token is invalid. Please login again.\"}";
response.getWriter().write(jsonResponse);
return;
}
email = jwtUtil.extractEmail(jwt);
} catch (ExpiredJwtException expiredJwtException) {
logger.error(expiredJwtException.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(new RequestResponseDTO("Authentication token is expired. Please login again.").toJSON());
return;
} catch (Exception e) {
logger.error(e.getMessage());
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().write(new RequestResponseDTO(e.getMessage()).toJSON());
return;
}
}
if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) {
String role = jwtUtil.extractRole(jwt);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
email, null, Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role)));
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
// logger.info("Token of user " + jwtUtil.extractEmail(jwt) + (jwtUtil.isTokenExpired(jwt) ? " is expired" : " is not expired"));
chain.doFilter(request, response);
}
}

View File

@@ -0,0 +1,101 @@
package _11.asktpk.artisanconnectbackend.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@Component
public class JwtUtil {
@Value("${jwt.secret:defaultSecretKeyNeedsToBeAtLeast32BytesLong}")
private String secret;
@Value("${jwt.expiration}")
private long expiration;
// sterowanie tokenami wygasnietymi
private final Set<String> blacklistedTokens = ConcurrentHashMap.newKeySet();
public void blacklistToken(String token) {
blacklistedTokens.add(token);
}
public boolean isBlacklisted(String token) {
return blacklistedTokens.contains(token);
}
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
private final Map<String, String> userActiveTokens = new ConcurrentHashMap<>();
public boolean isLatestToken(String token) {
String email = extractEmail(token);
String tokenId = extractTokenId(token);
String latestTokenId = userActiveTokens.get(email);
return latestTokenId != null && latestTokenId.equals(tokenId);
}
public String generateToken(String email, String role, Long userId) {
Map<String, Object> claims = new HashMap<>();
claims.put("role", role);
claims.put("userId", userId);
claims.put("tokenId", UUID.randomUUID().toString());
String token = createToken(claims, email);
userActiveTokens.put(email, extractTokenId(token));
return token;
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public String extractTokenId(String token) {
return extractAllClaims(token).get("tokenId", String.class);
}
public String extractEmail(String token) {
return extractClaim(token, Claims::getSubject);
}
public String extractRole(String token) {
return extractAllClaims(token).get("role", String.class);
}
public Long extractUserId(String token) {
return extractAllClaims(token).get("userId", Long.class);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
}

View File

@@ -1,9 +1,14 @@
package _11.asktpk.artisanconnectbackend.service;
import _11.asktpk.artisanconnectbackend.dto.AuthRequestDTO;
import _11.asktpk.artisanconnectbackend.dto.ClientDTO;
import _11.asktpk.artisanconnectbackend.dto.ClientRegistrationDTO;
import _11.asktpk.artisanconnectbackend.entities.Client;
import _11.asktpk.artisanconnectbackend.entities.Role;
import _11.asktpk.artisanconnectbackend.repository.ClientRepository;
import _11.asktpk.artisanconnectbackend.repository.RolesRepository;
import jakarta.persistence.EntityNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
@@ -11,19 +16,27 @@ import java.util.List;
@Service
public class ClientService {
private final ClientRepository clientRepository;
private final PasswordEncoder passwordEncoder;
private final RolesRepository rolesRepository;
public ClientService(ClientRepository clientRepository) {
public ClientService(ClientRepository clientRepository, PasswordEncoder passwordEncoder, RolesRepository rolesRepository) {
this.clientRepository = clientRepository;
this.passwordEncoder = passwordEncoder;
this.rolesRepository = rolesRepository;
}
private ClientDTO toDto(Client client) {
if(client == null) {
return null;
}
ClientDTO dto = new ClientDTO();
dto.setId(client.getId());
dto.setFirstName(client.getFirstName());
dto.setLastName(client.getLastName());
dto.setEmail(client.getEmail());
dto.setRole(client.getRole());
dto.setRole(client.getRole().getRole());
dto.setImage(client.getImage());
return dto;
@@ -31,17 +44,35 @@ public class ClientService {
private Client fromDto(ClientDTO dto) {
Client client = new Client();
Role rola;
if (clientRepository.findById(dto.getId()).isPresent()) {
rola = clientRepository.findById(dto.getId()).get().getRole();
} else {
rola = new Role();
rola.setRole("USER");
}
client.setId(dto.getId());
client.setFirstName(dto.getFirstName());
client.setLastName(dto.getLastName());
client.setEmail(dto.getEmail());
client.setRole(dto.getRole());
client.setRole(rola);
client.setImage(dto.getImage());
return client;
}
private Client fromDto(ClientRegistrationDTO dto) {
Client client = new Client();
client.setFirstName(dto.getFirstName());
client.setLastName(dto.getLastName());
client.setEmail(dto.getEmail());
client.setPassword(dto.getPassword());
return client;
}
public List<ClientDTO> getAllClients() {
List<Client> clients = clientRepository.findAll();
return clients.stream().map(this::toDto).toList();
@@ -51,6 +82,10 @@ public class ClientService {
return clientRepository.findById(id).orElse(null);
}
public ClientDTO getClientByIdDTO(Long id) {
return toDto(clientRepository.findById(id).orElse(null));
}
public boolean clientExists(Long id) {
return clientRepository.existsById(id);
}
@@ -59,15 +94,21 @@ public class ClientService {
return toDto(clientRepository.save(fromDto(clientDTO)));
}
public Client saveClientToDB(Client client) {
return clientRepository.save(client);
}
public ClientDTO updateClient(long id, ClientDTO clientDTO) {
Client existingClient = clientRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Nie znaleziono ogłoszenia o ID: " + id));
Role newRole = rolesRepository.findRoleByRole(clientDTO.getRole());
existingClient.setEmail(clientDTO.getEmail());
existingClient.setFirstName(clientDTO.getFirstName());
existingClient.setLastName(clientDTO.getLastName());
existingClient.setImage(clientDTO.getImage());
existingClient.setRole(clientDTO.getRole());
existingClient.setRole(newRole);
return toDto(clientRepository.save(existingClient));
}
@@ -75,4 +116,30 @@ public class ClientService {
public void deleteClient(Long id) {
clientRepository.deleteById(id);
}
// И замените метод checkClientCredentials на:
public boolean checkClientCredentials(AuthRequestDTO dto) {
Client cl = clientRepository.findByEmail(dto.getEmail());
if (cl == null) {
return false;
}
return passwordEncoder.matches(dto.getPassword(), cl.getPassword());
}
// При создании нового пользователя не забудьте шифровать пароль:
public ClientDTO registerClient(ClientRegistrationDTO clientDTO) {
Client client = fromDto(clientDTO);
client.setRole(rolesRepository.findRoleById(1L));
client.setPassword(passwordEncoder.encode(client.getPassword()));
return toDto(clientRepository.save(client));
}
public Client getClientByEmail(String email) {
return clientRepository.findByEmail(email);
}
public Role getUserRole() {
return rolesRepository.findRoleByRole("USER");
}
}

View File

@@ -0,0 +1,24 @@
package _11.asktpk.artisanconnectbackend.service;
import _11.asktpk.artisanconnectbackend.dto.EmailDTO;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
private final JavaMailSender mailSender;
public EmailService(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
public void sendEmail(EmailDTO email) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email.getTo());
message.setSubject(email.getSubject());
message.setText(email.getBody());
message.setFrom("patryk.kania001@gmail.com");
mailSender.send(message);
}
}

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

@@ -1,43 +1,47 @@
package _11.asktpk.artisanconnectbackend.service;
import _11.asktpk.artisanconnectbackend.dto.AttributeDto;
import _11.asktpk.artisanconnectbackend.dto.NoticeRequestDTO;
import _11.asktpk.artisanconnectbackend.entities.AttributesNotice;
import _11.asktpk.artisanconnectbackend.entities.Client;
import _11.asktpk.artisanconnectbackend.entities.Notice;
import _11.asktpk.artisanconnectbackend.repository.ClientRepository;
import _11.asktpk.artisanconnectbackend.repository.NoticeRepository;
import _11.asktpk.artisanconnectbackend.dto.NoticeDTO;
import _11.asktpk.artisanconnectbackend.dto.NoticeResponseDTO;
import jakarta.persistence.EntityNotFoundException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
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.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Service
public class NoticeService {
private static final Logger logger = LogManager.getLogger(NoticeService.class);
@Value("${file.upload-dir}")
private String uploadDir;
private final NoticeRepository noticeRepository;
private final ClientRepository clientRepository;
private final ImageService imageService;
public NoticeService(NoticeRepository noticeRepository, ClientRepository clientRepository) {
public NoticeService(NoticeRepository noticeRepository, ClientRepository clientRepository, ImageService imageService) {
this.noticeRepository = noticeRepository;
this.clientRepository = clientRepository;
this.imageService = imageService;
}
private Notice fromDTO(NoticeDTO dto) {
public Notice fromDTO(NoticeRequestDTO dto) {
Notice notice = new Notice();
notice.setTitle(dto.getTitle());
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());
Client client = clientRepository.findById(dto.getClientId())
.orElseThrow(() -> new EntityNotFoundException("Nie znaleziono klienta o ID: " + dto.getClientId()));
@@ -46,45 +50,61 @@ public class NoticeService {
return notice;
}
private NoticeDTO toDTO(Notice notice) {
NoticeDTO dto = new NoticeDTO();
private NoticeResponseDTO toDTO(Notice notice) {
NoticeResponseDTO dto = new NoticeResponseDTO();
dto.setNoticeId(notice.getIdNotice());
dto.setTitle(notice.getTitle());
dto.setClientId(notice.getClient().getId());
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());
List<AttributeDto> attributes = new ArrayList<>();
if (notice.getAttributesNotices() != null) {
for (AttributesNotice an : notice.getAttributesNotices()) {
AttributeDto attr = new AttributeDto();
attr.setName(an.getAttributeValue().getAttribute().getName());
attr.setValue(an.getAttributeValue().getValue());
attributes.add(attr);
}
}
dto.setAttributes(attributes);
return dto;
}
public List<NoticeDTO> getAllNotices() {
List<NoticeDTO> result = new ArrayList<>();
public List<NoticeResponseDTO> getAllNotices() {
List<NoticeResponseDTO> result = new ArrayList<>();
for (Notice notice : noticeRepository.findAll()) {
result.add(toDTO(notice));
}
return result;
}
public NoticeDTO getNoticeById(Long id) {
public NoticeResponseDTO getNoticeById(Long id) {
Notice notice = noticeRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Nie znaleziono ogłoszenia o ID: " + id));
return toDTO(notice);
}
public void addNotice(NoticeDTO dto) {
noticeRepository.save(fromDTO(dto));
public Notice getNoticeByIdEntity(Long id) {
return noticeRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Nie znaleziono ogłoszenia o ID: " + id));
}
public Long addNotice(NoticeRequestDTO dto) {
Notice notice = fromDTO(dto);
notice.setPublishDate(LocalDateTime.now());
return noticeRepository.save(notice).getIdNotice();
}
public boolean noticeExists(Long id) {
return noticeRepository.existsById(id);
}
public NoticeDTO updateNotice(Long id, NoticeDTO dto) {
public NoticeResponseDTO updateNotice(Long id, NoticeRequestDTO dto) {
Notice existingNotice = noticeRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Nie znaleziono ogłoszenia o ID: " + id));
@@ -92,9 +112,7 @@ 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());
if (dto.getClientId() != null && !dto.getClientId().equals(existingNotice.getClient().getId())) {
Client client = clientRepository.findById(dto.getClientId())
@@ -108,55 +126,40 @@ public class NoticeService {
public void deleteNotice(Long id) {
if (noticeExists(id)) {
noticeRepository.deleteById(id);
List<String> imagesList = new ArrayList<>();
try {
imagesList = imageService.getImagesList(id);
} catch (Exception e) {
logger.info("There weren't any images for notice with ID: " + id + ". Skipping deletion of images. Message: " + e.getMessage());
}
try {
for (String imageName : imagesList) {
imageService.deleteImage(uploadDir, imageName);
}
} catch (Exception e) {
logger.info("There were some issues while deleting images for notice with ID: " + id + ". Message: " + e.getMessage());
}
} else {
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);
public boolean isNoticeOwnedByClient(long noticeId, long clientId) {
return noticeRepository.existsByIdNoticeAndClientId(noticeId, clientId);
}
public void boostNotice(long noticeId) {
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);
.orElseThrow(() -> new EntityNotFoundException("Ogłoszenie o ID " + noticeId + " nie istnieje."));
notice.setPublishDate(LocalDateTime.now());
noticeRepository.save(notice);
return filePath.toString();
}
}

View File

@@ -0,0 +1,83 @@
package _11.asktpk.artisanconnectbackend.service;
import _11.asktpk.artisanconnectbackend.dto.OrderDTO;
import _11.asktpk.artisanconnectbackend.entities.Client;
import _11.asktpk.artisanconnectbackend.entities.Notice;
import _11.asktpk.artisanconnectbackend.repository.ClientRepository;
import _11.asktpk.artisanconnectbackend.repository.NoticeRepository;
import _11.asktpk.artisanconnectbackend.repository.OrderRepository;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import jakarta.persistence.EntityNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import _11.asktpk.artisanconnectbackend.entities.Order;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final ClientRepository clientRepository;
private final NoticeRepository noticeRepository;
@Autowired
public OrderService(OrderRepository orderRepository, ClientRepository clientRepository, NoticeRepository noticeRepository) {
this.orderRepository = orderRepository;
this.clientRepository = clientRepository;
this.noticeRepository = noticeRepository;
}
public Order fromDTO(OrderDTO orderDTO) {
Order order = new Order();
order.setOrderType(orderDTO.getOrderType());
order.setStatus(Enums.OrderStatus.PENDING);
if(orderDTO.getOrderType() == Enums.OrderType.ACTIVATION){
order.setAmount(10.00);
}else{
order.setAmount(8.00);
}
order.setCreatedAt(LocalDateTime.now()
);
order.setUpdatedAt(LocalDateTime.now()
);
Client client = clientRepository.findById(orderDTO.getClientId())
.orElseThrow(() -> new EntityNotFoundException("Nie znaleziono klienta o ID: " + orderDTO.getClientId()));
order.setClient(client);
Notice notice = noticeRepository.findById(orderDTO.getNoticeId())
.orElseThrow(() -> new EntityNotFoundException("Nie znaleziono ogłoszenia o ID: " + orderDTO.getNoticeId()));
order.setNotice(notice);
return order;
}
public Long addOrder(OrderDTO orderDTO) {
return orderRepository.save(fromDTO(orderDTO)).getId();
}
public Long changeOrderStatus(Long id, Enums.OrderStatus status) {
Order order = orderRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Nie znaleziono zamówienia o ID: " + id));
order.setStatus(status);
order = orderRepository.save(order);
return order.getId();
}
public Order getOrderById(Long id) {
return orderRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Nie znaleziono zamówienia o ID: " + id));
}
public List<Order> getOrdersByClientId(Long clientId) {
return orderRepository.findByClientId(clientId);
}
}

View File

@@ -0,0 +1,91 @@
package _11.asktpk.artisanconnectbackend.service;
import _11.asktpk.artisanconnectbackend.dto.OAuthPaymentResponseDTO;
import _11.asktpk.artisanconnectbackend.dto.TransactionPaymentRequestDTO;
import _11.asktpk.artisanconnectbackend.dto.TransactionPaymentResponseDTO;
import _11.asktpk.artisanconnectbackend.entities.Order;
import _11.asktpk.artisanconnectbackend.entities.Payment;
import _11.asktpk.artisanconnectbackend.repository.PaymentRepository;
import _11.asktpk.artisanconnectbackend.utils.Enums;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.List;
@Service
public class PaymentService {
private final WebClient webClient;
private final String clientId;
private final String clientSecret;
private final String authUrl;
private final String transactionUrl;
private final PaymentRepository paymentRepository;
public PaymentService(
WebClient.Builder webClientBuilder,
@Value("${tpay.clientId}") String clientId,
@Value("${tpay.clientSecret}") String clientSecret,
@Value("${tpay.authUrl}") String authUrl,
@Value("${tpay.transactionUrl}") String transactionUrl,
PaymentRepository paymentRepository
) {
this.webClient = webClientBuilder.baseUrl(authUrl).build();
this.clientId = clientId;
this.clientSecret = clientSecret;
this.authUrl = authUrl;
this.transactionUrl = transactionUrl;
this.paymentRepository = paymentRepository;
}
public OAuthPaymentResponseDTO getOAuthToken() {
return webClient.post()
.uri("")
.contentType(MediaType.MULTIPART_FORM_DATA)
.header("accept", "application/json")
.body(BodyInserters.fromMultipartData("client_id", clientId)
.with("client_secret", clientSecret))
.retrieve()
.bodyToMono(OAuthPaymentResponseDTO.class)
.block();
}
public String createTransaction(Order order, String accessToken, TransactionPaymentRequestDTO transactionPaymentRequestDTO) {
TransactionPaymentResponseDTO response = webClient.post()
.uri(transactionUrl)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(transactionPaymentRequestDTO)
.retrieve()
.bodyToMono(TransactionPaymentResponseDTO.class)
.block();
if (response != null && "success".equalsIgnoreCase(response.getResult())) {
Payment payment = new Payment();
payment.setOrder(order);
payment.setAmount(response.getAmount());
payment.setStatus(Enums.PaymentStatus.PENDING);
payment.setTransactionId(response.getTitle());
payment.setTransactionPaymentUrl(response.getTransactionPaymentUrl());
paymentRepository.save(payment);
System.out.println(response);
return response.getTransactionPaymentUrl();
}
return null;
}
public List<Payment> getPaymentsByOrderId(Long id) {
return paymentRepository.findAllByOrderId(id);
}
}

View File

@@ -0,0 +1,66 @@
package _11.asktpk.artisanconnectbackend.service;
import _11.asktpk.artisanconnectbackend.dto.WishlistDTO;
import _11.asktpk.artisanconnectbackend.dto.NoticeResponseDTO;
import _11.asktpk.artisanconnectbackend.entities.Client;
import _11.asktpk.artisanconnectbackend.entities.Notice;
import _11.asktpk.artisanconnectbackend.entities.Wishlist;
import _11.asktpk.artisanconnectbackend.repository.WishlistRepository;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class WishlistService {
private final WishlistRepository wishlistRepository;
private final NoticeService noticeService;
public WishlistService(WishlistRepository wishlistRepository, @Lazy NoticeService noticeService) {
this.wishlistRepository = wishlistRepository;
this.noticeService = noticeService;
}
public List<WishlistDTO> getWishlistForClientId(Long clientId) {
List<Wishlist> wishlistEntities = wishlistRepository.findAllByClientId(clientId);
return wishlistEntities.stream()
.map(this::toDTO)
.toList();
}
public boolean toggleWishlist(Client client, Notice notice) {
Optional<Wishlist> existingEntry = wishlistRepository.findByClientAndNotice(client, notice);
if (existingEntry.isPresent()) {
wishlistRepository.delete(existingEntry.get());
return false;
} else {
Wishlist wishlist = new Wishlist();
wishlist.setClient(client);
wishlist.setNotice(notice);
wishlistRepository.save(wishlist);
return true;
}
}
private WishlistDTO toDTO(Wishlist wishlist) {
WishlistDTO dto = new WishlistDTO();
dto.setId(wishlist.getId());
dto.setClientId(wishlist.getClient().getId());
dto.setNoticeId(wishlist.getNotice().getIdNotice());
return dto;
}
public List<NoticeResponseDTO> getNoticesInWishlist(Long clientId) {
List<Wishlist> wishlistEntries = wishlistRepository.findAllByClientId(clientId);
return wishlistEntries.stream()
.map(wishlist -> noticeService.getNoticeById(wishlist.getNotice().getIdNotice()))
.collect(Collectors.toList());
}
}

View File

@@ -1,5 +1,7 @@
package _11.asktpk.artisanconnectbackend.utils;
import java.util.Map;
public class Enums {
public enum Role {
ADMIN, USER
@@ -13,7 +15,48 @@ 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
}
public enum OrderType {
ACTIVATION,
BOOST
}
public enum OrderStatus {
PENDING, COMPLETED, CANCELLED
}
public enum PaymentStatus{
PENDING, CORRECT, INCORRECT
}
}

View File

@@ -0,0 +1,24 @@
package _11.asktpk.artisanconnectbackend.utils;
import _11.asktpk.artisanconnectbackend.security.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
@Component
public class Tools {
private final JwtUtil jwtUtil;
public Tools(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
public Long getClientIdFromRequest(HttpServletRequest request) {
String authorizationHeader = request.getHeader("Authorization");
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
return jwtUtil.extractUserId(authorizationHeader.substring(7));
} else {
return -1L;
}
}
}

View File

@@ -2,14 +2,38 @@ 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
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=patryk.kania001@gmail.com
spring.mail.password=pmyd ylwg mbsn hcpp
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
tpay.clientId = 01JQKC048X62ST9V59HNRSXD92-01JQKC2CQHPYXQFSFX8BKC24BX
tpay.clientSecret = 44898642be53381cdcc47f3e44bf5a15e592f5d270fc3a6cf6fb81a8b8ebffb9
tpay.authUrl = https://openapi.sandbox.tpay.com/oauth/auth
tpay.transactionUrl = https://openapi.sandbox.tpay.com/transactions
tpay.securityCode = )IY7E)YSM!A)Q6O-GN#U7U_33s9qObk8
#jwt settings
jwt.secret=DIXLsOs3FKmCAQwISd0SKsHMXJrPl3IKIRkVlkOvYW7kEcdUTbxh8zFe1B3eZWkY
jwt.expiration=300000
logging.file.name=logs/payment-notifications.log
logging.level.TpayLogger=INFO

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

@@ -1,10 +1,15 @@
INSERT INTO clients (email, first_name, image, last_name, password, role)
INSERT INTO roles (id, rolename)
VALUES
('dignissim.tempor.arcu@aol.ca', 'Diana', 'null', 'Harrison', 'password', 'USER'),
('john.doe@example.com', 'John', 'null', 'Doe', 'password123', 'ADMIN'),
('jane.smith@example.com', 'Jane', 'null', 'Smith', 'securepass', 'USER'),
('michael.brown@example.com', 'Michael', 'null', 'Brown', 'mypassword', 'USER'),
('emily.jones@example.com', 'Emily', 'null', 'Jones', 'passw0rd', 'USER');
(1, 'USER'),
(2, 'ADMIN');
INSERT INTO clients (email, first_name, last_name, password, role_id)
VALUES
('dignissim.tempor.arcu@aol.ca', 'Diana', 'Harrison', 'password', 1),
('john.doe@example.com', 'John', 'Doe', 'password123', 2),
('jane.smith@example.com', 'Jane', 'Smith', 'securepass', 1),
('michael.brown@example.com', 'Michael', 'Brown', 'mypassword', 1),
('emily.jones@example.com', 'Emily', 'Jones', 'passw0rd', 1);
INSERT INTO notice (title, description, client_id, price, category, status, publish_date) VALUES
@@ -12,4 +17,45 @@ INSERT INTO notice (title, description, client_id, price, category, status, publ
('Drewniany stół', 'Solidny stół wykonany z litego drewna dębowego.', 3, 1200.00, 'Furniture', 'ACTIVE', '2023-09-15'),
('Ceramiczna waza', 'Piękna waza ceramiczna, idealna na prezent.', 2, 300.00, 'Ceramics', 'INACTIVE', '2023-08-20'),
('Obraz olejny', 'Obraz olejny przedstawiający krajobraz górski.', 4, 800.00, 'Painting', 'ACTIVE', '2023-07-10'),
('Skórzany portfel', 'Ręcznie wykonany portfel ze skóry naturalnej.', 1, 250.00, 'Leatherwork', 'ACTIVE', '2023-06-05');
('Skórzany portfel', 'Ręcznie wykonany portfel ze skóry naturalnej.', 1, 250.00, 'Leatherwork', 'ACTIVE', '2023-06-05');
insert into attributes (name) values
('Kolor'),
('Materiał');
-- Kolory
insert into attribute_values (value, id_attribute) values
('Zielony', 1),
('Czerwony', 1),
('Niebieski', 1),
('Żółty', 1),
('Biały', 1),
('Czarny', 1),
('Różowy', 1),
('Szary', 1),
('Fioletowy', 1),
('Pomarańczowy', 1),
('Inny', 1);
-- Materiały
insert into attribute_values (value, id_attribute) values
('Bawełna', 2),
('Wełna', 2),
('Syntetyk', 2),
('Skóra', 2),
('Len', 2),
('Jedwab', 2),
('Poliester', 2),
('Akryl', 2),
('Wiskoza', 2),
('Nylon', 2),
('Inny', 2);
insert into attributes_notice (id_value, notice_id) values
(1, 1), -- Ręcznie robiona biżuteria - Zielony
(22, 1),
(2, 2), -- Drewniany stół - Czerwony
(3, 3), -- Ceramiczna waza - Niebieski
(4, 4), -- Obraz olejny - Żółty
(5, 5); -- Skórzany portfel - Biały

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>