diff --git a/Backend-feedme/feedme/.gitignore b/Backend-feedme/feedme/.gitignore new file mode 100644 index 0000000..4125023 --- /dev/null +++ b/Backend-feedme/feedme/.gitignore @@ -0,0 +1,39 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +.env + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/Backend-feedme/feedme/Dockerfile b/Backend-feedme/feedme/Dockerfile index 58c8dbd..317e265 100644 --- a/Backend-feedme/feedme/Dockerfile +++ b/Backend-feedme/feedme/Dockerfile @@ -20,11 +20,8 @@ RUN chmod +x ./gradlew # Build the application and skip tests RUN ./gradlew clean build -x test -# Copy the built JAR file to the app directory -# COPY build/libs/feedme-0.0.1-SNAPSHOT.jar /app/MailService.jar +# Set the entry point to run the JAR file with the specified port +ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "-Dserver.port=8085", "/app/build/libs/feedme-0.0.1-SNAPSHOT.jar"] -# Set the entry point to run the JAR file -ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "/app/build/libs/feedme-0.0.1-SNAPSHOT.jar"] - -# Expose port 8080 -EXPOSE 8080 +# Expose port 8085.. +EXPOSE 8085 diff --git a/Backend-feedme/feedme/build.gradle b/Backend-feedme/feedme/build.gradle index ec92ef2..bed2937 100644 --- a/Backend-feedme/feedme/build.gradle +++ b/Backend-feedme/feedme/build.gradle @@ -37,18 +37,31 @@ dependencies { // table 보이기 implementation("com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.1") - //스웨거 안됌 ㅠ - implementation 'org.springdoc:springdoc-openapi-ui:1.7.0' - implementation 'org.springdoc:springdoc-openapi-data-rest:1.7.0' - implementation 'org.springdoc:springdoc-openapi-security:1.7.0' + // 시큐리티 설정 + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' + testImplementation 'org.springframework.security:spring-security-test' - implementation 'io.springfox:springfox-boot-starter:3.0.0' - implementation 'io.springfox:springfox-swagger-ui:3.0.0' - //스웨거 끝 + //스웨거 + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' //MongoDB implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' + //JWT + testImplementation 'org.projectlombok:lombok' + implementation 'io.jsonwebtoken:jjwt:0.9.1' + implementation 'javax.xml.bind:jaxb-api:2.3.1' + + //OAuth 2.0 + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + + //redis + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + + // .env 받아오기 + implementation 'io.github.cdimascio:java-dotenv:5.2.2' + //JUnit4 추가 testImplementation("org.junit.vintage:junit-vintage-engine") { exclude group: "org.hamcrest", module: "hamcrest-core" diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/AlarmCheckRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/AlarmCheckRequestDTO.java new file mode 100644 index 0000000..5c4b80c --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/AlarmCheckRequestDTO.java @@ -0,0 +1,10 @@ +package com.todoslave.feedme.DTO; + +import lombok.Getter; + +@Getter +public class AlarmCheckRequestDTO { + + private String checkTime; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/AlarmRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/AlarmRequestDTO.java new file mode 100644 index 0000000..6367cea --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/AlarmRequestDTO.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.DTO; + +import lombok.Getter; + +@Getter +public class AlarmRequestDTO { + + private String token; + private String message; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureInfoResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureInfoResponseDTO.java new file mode 100644 index 0000000..23dc755 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureInfoResponseDTO.java @@ -0,0 +1,15 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.sql.Timestamp; + +@Data +public class CreatureInfoResponseDTO { + + private String name; + private String img; + private int exp; + private int day; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureMakeRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureMakeRequestDTO.java new file mode 100644 index 0000000..598a81b --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureMakeRequestDTO.java @@ -0,0 +1,14 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +@Data +public class CreatureMakeRequestDTO { + + String creatureName; + + String keyword; + +// MultipartFile photo; + String photo; +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureTodoDailyRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureTodoDailyRequestDTO.java new file mode 100644 index 0000000..c17e561 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureTodoDailyRequestDTO.java @@ -0,0 +1,13 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class CreatureTodoDailyRequestDTO { + + private LocalDate date; + private int next; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureTodoResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureTodoResponseDTO.java new file mode 100644 index 0000000..ad40a08 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CreatureTodoResponseDTO.java @@ -0,0 +1,17 @@ +package com.todoslave.feedme.DTO; + +import lombok.Builder; +import lombok.Data; + +import java.time.LocalDate; + +@Builder +@Data +public class CreatureTodoResponseDTO { + private int id; + private String content; + private LocalDate createdAt; + private int isCompleted; + + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CretureTodoRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CretureTodoRequestDTO.java new file mode 100644 index 0000000..b8e8784 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/CretureTodoRequestDTO.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class CretureTodoRequestDTO { + private LocalDate date; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedCommentDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedCommentDTO.java new file mode 100644 index 0000000..ac8fa62 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedCommentDTO.java @@ -0,0 +1,13 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.util.List; + +@Data +public class FeedCommentDTO { + private String name; + private String comment; + private String time; + private List replies; +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedCommentRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedCommentRequestDTO.java new file mode 100644 index 0000000..cb54d06 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedCommentRequestDTO.java @@ -0,0 +1,8 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +@Data +public class FeedCommentRequestDTO { + private String content; +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedCommentResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedCommentResponseDTO.java new file mode 100644 index 0000000..d40ad8f --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedCommentResponseDTO.java @@ -0,0 +1,12 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class FeedCommentResponseDTO { + private String nickname; + private String content; + private LocalDateTime createdAt; +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedDTO.java new file mode 100644 index 0000000..d72d7de --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedDTO.java @@ -0,0 +1,17 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.util.List; + +@Data +public class FeedDTO { + private int id; + private String name; + private String img; + private String caption; + private String time; + private int likes; + private List comments; + } + diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedModifyRequest.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedModifyRequest.java new file mode 100644 index 0000000..effca46 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedModifyRequest.java @@ -0,0 +1,8 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +@Data +public class FeedModifyRequest { + private String content; +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedReplyDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedReplyDTO.java new file mode 100644 index 0000000..a170504 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedReplyDTO.java @@ -0,0 +1,10 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +@Data +public class FeedReplyDTO { + private String name; + private String comment; + private String time; +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedRequestDTO.java new file mode 100644 index 0000000..aab1a27 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedRequestDTO.java @@ -0,0 +1,14 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class FeedRequestDTO { + + private LocalDate diaryDate; //언제 + + private String content; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedResponseDTO.java new file mode 100644 index 0000000..74a0f03 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FeedResponseDTO.java @@ -0,0 +1,19 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.sql.Timestamp; + + +@Data +public class FeedResponseDTO { + + private int id; + private String img; + private String content; + private String author; + private String likeCnt; + + private Timestamp createdAt; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FriendReqRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FriendReqRequestDTO.java new file mode 100644 index 0000000..e12b511 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FriendReqRequestDTO.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.DTO; + +import lombok.Getter; + +@Getter +public class FriendReqRequestDTO { + + // 상대방 닉네임 + private String counterpartNickname; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FriendReqResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FriendReqResponseDTO.java new file mode 100644 index 0000000..f30883b --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FriendReqResponseDTO.java @@ -0,0 +1,14 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +@Data +public class FriendReqResponseDTO { + + // 친구 요청 ID + private int id; + + // 상대방 닉네임 + private String counterpartNickname; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FriendResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FriendResponseDTO.java new file mode 100644 index 0000000..9ce80a0 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/FriendResponseDTO.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +@Data +public class FriendResponseDTO { + + private int friendId; + private String counterpartNickname; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberChatBuildRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberChatBuildRequestDTO.java new file mode 100644 index 0000000..71f2e6a --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberChatBuildRequestDTO.java @@ -0,0 +1,12 @@ +//맴버 채팅 주석 + +//package com.todoslave.feedme.DTO; +// +//import lombok.Getter; +// +//@Getter +//public class ChatFriendCreateRequestDTO { +// +// private String CounterpartNickname; +// +//} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberChatMessageRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberChatMessageRequestDTO.java new file mode 100644 index 0000000..48327ab --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberChatMessageRequestDTO.java @@ -0,0 +1,12 @@ +//맴버 채팅 주석 + +//package com.todoslave.feedme.DTO; +// +//import lombok.Getter; +// +//@Getter +//public class ChatMessageRequestDTO { +// +// private String message; +// +//} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberChatMessageResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberChatMessageResponseDTO.java new file mode 100644 index 0000000..4a6627c --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberChatMessageResponseDTO.java @@ -0,0 +1,18 @@ +//맴버 채팅 주석 + +//package com.todoslave.feedme.DTO; +// +//import java.time.LocalDate; +//import java.time.LocalDateTime; +//import lombok.Data; +//import lombok.NoArgsConstructor; +// +//@Data +//@NoArgsConstructor +//public class ChatMessageResponseDTO { +// +// private String sendNickname; // 보내는 사람 닉네임 +// private String message; // 메세지 내용 +// private LocalDateTime transmitAt; // 전송 시간 +// +//} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSearchRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSearchRequestDTO.java new file mode 100644 index 0000000..7a9b9a7 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSearchRequestDTO.java @@ -0,0 +1,7 @@ +package com.todoslave.feedme.DTO; + +public class MemberSearchRequestDTO { + + // STring 이라서 굳이 안쓸듯 + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSearchResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSearchResponseDTO.java new file mode 100644 index 0000000..292c19f --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSearchResponseDTO.java @@ -0,0 +1,10 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +@Data +public class MemberSearchResponseDTO { + String nickname; + boolean isFriend; + String creatureImg; +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSignup.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSignup.java new file mode 100644 index 0000000..40732c0 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSignup.java @@ -0,0 +1,18 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.sql.Timestamp; + +@Data +public class MemberSignup { + + String email; + + String nickname; + + String userRole; + + Timestamp birthday; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSignupRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSignupRequestDTO.java new file mode 100644 index 0000000..9af8a4e --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MemberSignupRequestDTO.java @@ -0,0 +1,18 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.sql.Timestamp; + +@Data +public class MemberSignupRequestDTO { + + String email; + + String nickname; + + String userRole = "ROLE_USER"; + + Timestamp birthday; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MypageResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MypageResponseDTO.java new file mode 100644 index 0000000..f76088a --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/MypageResponseDTO.java @@ -0,0 +1,18 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +import java.time.LocalDate; +@Data +public class MypageResponseDTO { + private String nickname; + private String email; + private LocalDate brithday; + + private int creatureId; + private String creatureName; + private int exp; + private int level; + private String image; + private int togetherDay; +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/PaginationRequest.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/PaginationRequestDTO.java similarity index 74% rename from Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/PaginationRequest.java rename to Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/PaginationRequestDTO.java index 86abedc..ab96fdf 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/PaginationRequest.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/PaginationRequestDTO.java @@ -3,7 +3,7 @@ import lombok.Data; @Data -public class PaginationRequest { +public class PaginationRequestDTO { private int skip; private int limit; diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCalendarResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCalendarResponseDTO.java new file mode 100644 index 0000000..c2d5407 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCalendarResponseDTO.java @@ -0,0 +1,14 @@ +package com.todoslave.feedme.DTO; + +import java.time.LocalDate; +import lombok.Data; + +@Data +public class TodoCalendarResponseDTO { + + private LocalDate date; + private int inCompleted; + private int completed; + private int total; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCategoryRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCategoryRequestDTO.java new file mode 100644 index 0000000..bae5591 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCategoryRequestDTO.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.DTO; + +import lombok.Getter; + +@Getter +public class TodoCategoryRequestDTO { + + private int id; + private String name; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCategoryResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCategoryResponseDTO.java new file mode 100644 index 0000000..7a1db39 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCategoryResponseDTO.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +@Data +public class TodoCategoryResponseDTO { + + private int id; + private String name; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCreateRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCreateRequestDTO.java new file mode 100644 index 0000000..1f8257b --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoCreateRequestDTO.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.DTO; + +import lombok.Getter; + +@Getter +public class TodoCreateRequestDTO { + + private String content; + private int categoryId; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoDailyRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoDailyRequestDTO.java new file mode 100644 index 0000000..1548247 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoDailyRequestDTO.java @@ -0,0 +1,12 @@ +package com.todoslave.feedme.DTO; + +import java.time.LocalDate; +import lombok.Getter; + +@Getter +public class TodoDailyRequestDTO { + + private LocalDate date; + private int next; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoMainResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoMainResponseDTO.java new file mode 100644 index 0000000..a5c3618 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoMainResponseDTO.java @@ -0,0 +1,12 @@ +package com.todoslave.feedme.DTO; + +import lombok.Data; + +@Data +public class TodoMainResponseDTO { + + private int id; + private String content; + private int isCompleted; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoModifyRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoModifyRequestDTO.java new file mode 100644 index 0000000..08f3220 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoModifyRequestDTO.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.DTO; + +import lombok.Getter; + +@Getter +public class TodoModifyRequestDTO { + + private int id; + private String content; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoRequestDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoRequestDTO.java new file mode 100644 index 0000000..db3556e --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoRequestDTO.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.DTO; + +import java.time.LocalDate; +import lombok.Getter; + +@Getter +public class TodoRequestDTO { + + private LocalDate date; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoResponseDTO.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoResponseDTO.java new file mode 100644 index 0000000..382a3d0 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/DTO/TodoResponseDTO.java @@ -0,0 +1,16 @@ +package com.todoslave.feedme.DTO; + +import java.time.LocalDate; +import lombok.Data; + +@Data +public class TodoResponseDTO { + + private int id; + private int categoryId; + private String categoryName; + private String content; + private LocalDate createdAt; + private int isCompleted; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/FeedmeApplication.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/FeedmeApplication.java index 98e61a1..abe624d 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/FeedmeApplication.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/FeedmeApplication.java @@ -1,13 +1,8 @@ package com.todoslave.feedme; -import com.todoslave.feedme.domain.entity.membership.Emotion; -import com.todoslave.feedme.domain.entity.membership.Member; -import com.todoslave.feedme.service.MemberService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.CommandLineRunner; +import io.github.cdimascio.dotenv.Dotenv; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; import org.springframework.scheduling.annotation.EnableScheduling; @@ -16,32 +11,21 @@ @EnableJpaRepositories(basePackages = "com.todoslave.feedme.repository") @EnableMongoRepositories(basePackages = "com.todoslave.feedme.repository") @EnableScheduling -public class FeedmeApplication implements CommandLineRunner { - - private final MemberService memberService; - - @Autowired - public FeedmeApplication(MemberService memberService) { - this.memberService = memberService; - } +public class FeedmeApplication { public static void main(String[] args) { + // Load environment variables from .env file + Dotenv dotenv = Dotenv.load(); + System.setProperty("NAVER_CLIENT_ID", dotenv.get("NAVER_CLIENT_ID")); + System.setProperty("NAVER_CLIENT_SECRET", dotenv.get("NAVER_CLIENT_SECRET")); + System.setProperty("KAKAO_CLIENT_ID", dotenv.get("KAKAO_CLIENT_ID")); + System.setProperty("KAKAO_CLIENT_SECRET", dotenv.get("KAKAO_CLIENT_SECRET")); + System.setProperty("DB_USERNAME", dotenv.get("DB_USERNAME")); + System.setProperty("DB_PASSWORD", dotenv.get("DB_PASSWORD")); + System.setProperty("JWT_SECRET_KEY", dotenv.get("JWT_SECRET_KEY")); + + // Run the Spring Boot application SpringApplication.run(FeedmeApplication.class, args); } - @Override - public void run(String... args) throws Exception { - Member member = new Member(); - - member.setEmail("asdf@gmail.com"); - member.setToken("ABC"); - member.setExp(0); - member.setStatus(Emotion.SAD); - member.setNickname("테스트1"); - member.setLatitude(12.4); - member.setLongitude(12.4); - - int saveId = memberService.Join(member); - System.out.println("Member saved with ID: " + saveId); - } } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/SwaggerConfig.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/SwaggerConfig.java index 160550d..104259e 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/SwaggerConfig.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/SwaggerConfig.java @@ -1,18 +1,35 @@ package com.todoslave.feedme.config; +import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SwaggerConfig { - @Bean - public OpenAPI customOpenAPI() { + public OpenAPI openAPI() { + String jwt = "JWT"; + SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwt); + Components components = new Components().addSecuritySchemes(jwt, new SecurityScheme() + .name(jwt) + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + ); return new OpenAPI() - .info(new Info().title("Feedme API") - .version("1.0") - .description("Feedme API documentation")); + .components(new Components()) + .info(apiInfo()) + .addSecurityItem(securityRequirement) + .components(components); + } + private Info apiInfo() { + return new Info() + .title("API Test") // API의 제목 + .description("Let's practice Swagger UI") // API에 대한 설명 + .version("1.0.0"); // API의 버전 } } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/TransactionManagerConfig.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/TransactionManagerConfig.java index ec9ce04..3135b43 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/TransactionManagerConfig.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/TransactionManagerConfig.java @@ -1,17 +1,17 @@ -package com.todoslave.feedme.config; - - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; - -import javax.sql.DataSource; - -@Configuration -public class TransactionManagerConfig { - - @Bean - public DataSourceTransactionManager transactionManager(DataSource dataSource) { - return new DataSourceTransactionManager(dataSource); - } -} +//package com.todoslave.feedme.config; +// +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.jdbc.datasource.DataSourceTransactionManager; +// +//import javax.sql.DataSource; +// +//@Configuration +//public class TransactionManagerConfig { +// +// @Bean +// public DataSourceTransactionManager transactionManager(DataSource dataSource) { +// return new DataSourceTransactionManager(dataSource); +// } +//} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/WebSocketConfig.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/WebSocketConfig.java index aa43205..1bd62ac 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/WebSocketConfig.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/WebSocketConfig.java @@ -19,7 +19,7 @@ public void configureMessageBroker(MessageBrokerRegistry config) { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/friendChat") - .setAllowedOriginPatterns("http://localhost:8080", "null") // 명시적인 오리진 사용 + .setAllowedOriginPatterns("http://localhost:8080","*") // 명시적인 오리진 사용 .withSockJS(); } } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/jwt/JwtProperties.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/jwt/JwtProperties.java new file mode 100644 index 0000000..66072f4 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/jwt/JwtProperties.java @@ -0,0 +1,17 @@ +package com.todoslave.feedme.config.jwt; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Setter +@Getter +@Component +@ConfigurationProperties("jwt") +public class JwtProperties { + + private String issuer; + private String secretKey; +} + diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/RedisConfig.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/RedisConfig.java new file mode 100644 index 0000000..7f73431 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/RedisConfig.java @@ -0,0 +1,42 @@ +package com.todoslave.feedme.config.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; + +@Configuration +@EnableRedisRepositories +@RequiredArgsConstructor +public class RedisConfig { + + private final RedisProperties redisProperties; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); + redisStandaloneConfiguration.setHostName(redisProperties.getHost()); + redisStandaloneConfiguration.setPort(redisProperties.getPort()); + redisStandaloneConfiguration.setPassword(redisProperties.getPassword()); + return new LettuceConnectionFactory(redisStandaloneConfiguration); + } + + @Bean + public RedisTemplate redisTemplate() { + + // redisTemplate 를 받아와서 set, get, delete 를 사용 + RedisTemplate redisTemplate = new RedisTemplate<>(); + /* + * setKeySerializer, setValueSerializer 설정 + * redis-cli 을 통해 직접 데이터를 조회 시 알아볼 수 없는 형태로 출력되는 것을 방지 + */ + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + return redisTemplate; + } +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/RedisProperties.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/RedisProperties.java new file mode 100644 index 0000000..b51f69a --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/RedisProperties.java @@ -0,0 +1,15 @@ +package com.todoslave.feedme.config.security; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "spring.redis") +public class RedisProperties { + + private String host; + private int port; + private String password; +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/SecurityConfig.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/SecurityConfig.java new file mode 100644 index 0000000..3b5b6f7 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/SecurityConfig.java @@ -0,0 +1,78 @@ +package com.todoslave.feedme.config.security; + +import com.todoslave.feedme.config.security.filter.JwtAuthFilter; +import com.todoslave.feedme.config.security.filter.JwtExceptionFilter; +import com.todoslave.feedme.login.Handler.MyAuthenticationFailureHandler; +import com.todoslave.feedme.login.Handler.MyAuthenticationSuccessHandler; +import com.todoslave.feedme.login.Service.CustomOAuth2UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +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.CsrfConfigurer; +import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + private final MyAuthenticationSuccessHandler oAuth2LoginSuccessHandler; + private final CustomOAuth2UserService customOAuth2UserService; + private final JwtAuthFilter jwtAuthFilter; + private final MyAuthenticationFailureHandler oAuth2LoginFailureHandler; + private final JwtExceptionFilter jwtExceptionFilter; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + + http .httpBasic(HttpBasicConfigurer::disable) + .csrf(CsrfConfigurer::disable) + .cors(Customizer.withDefaults()) + + .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) +// .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 세션관리 정책을 STATELESS(세션이 있으면 쓰지도 않고, 없으면 만들지도 않는다) + .authorizeHttpRequests((requests) -> requests + .requestMatchers("/token/**").permitAll() + .requestMatchers("/", "/css/**","/images/**","/js/**","/favicon.ico","/h2-console/**").permitAll() + .requestMatchers("/login/oauth2/code/**","/login/**","/testsite" ,"/signup", "/user", "/v3/api-docs/**", "/swagger-ui/**","/creature", "/swagger-ui.html", "/users/**").permitAll() // 유저 설정 + .requestMatchers("/ws/**").permitAll() + .anyRequest().authenticated() + ) + .oauth2Login(oauth2 -> oauth2 + .loginPage("/test") + .userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService)) // OAuth2 로그인시 사용자 정보를 가져오는 엔드포인트와 사용자 서비스를 설정 + .failureHandler(oAuth2LoginFailureHandler) // OAuth2 로그인 실패시 처리할 핸들러를 지정해준다. + .successHandler(oAuth2LoginSuccessHandler) // OAuth2 로그인 성공시 처리할 핸들러를 지정해준다. + ); + + + + // JWT 인증 필터를 UsernamePasswordAuthenticationFilter 앞에 추가한다. + return http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(jwtExceptionFilter, UsernamePasswordAuthenticationFilter.class) + .build(); + } + + + @Bean + // CORS 설정 + CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.addAllowedOriginPattern("*"); + configuration.addAllowedMethod("*"); // 모든 HTTP 메서드 허용 + configuration.addAllowedHeader("*"); // 모든 헤더 허용 + configuration.setAllowCredentials(true); // 자격 증명 허용 설정 + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); // 모든 경로에 대해 CORS 구성 적용 + return source; + } + } \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/StatusResponseDto.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/StatusResponseDto.java new file mode 100644 index 0000000..319b656 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/StatusResponseDto.java @@ -0,0 +1,29 @@ +package com.todoslave.feedme.config.security; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; + +import lombok.Getter; + +@Getter +@AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) // DTO 를 JSON으로 변환 시 null값인 field 제외 +public class StatusResponseDto { + private Integer status; + private Object data; + + public StatusResponseDto(Integer status) { + this.status = status; + } + + public static StatusResponseDto addStatus(Integer status) { + return new StatusResponseDto(status); + } + + public static StatusResponseDto success(){ + return new StatusResponseDto(200); + } + public static StatusResponseDto success(Object data){ + return new StatusResponseDto(200, data); + } +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/WebConfig.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/WebConfig.java new file mode 100644 index 0000000..8c7621b --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/WebConfig.java @@ -0,0 +1,24 @@ +package com.todoslave.feedme.config.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig { + + @Bean + public WebMvcConfigurer corsConfigurer() { + return new WebMvcConfigurer() { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("http://localhost:3000") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(true); + } + }; + } +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/WebSecurityConfig.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/WebSecurityConfig.java new file mode 100644 index 0000000..7a921f7 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/WebSecurityConfig.java @@ -0,0 +1,297 @@ +////package com.todoslave.feedme.config.security; +////// +//////import com.todoslave.feedme.service.UserDetailService; +//////import lombok.RequiredArgsConstructor; +//////import org.springframework.context.annotation.Bean; +//////import org.springframework.context.annotation.Configuration; +//////import org.springframework.security.authentication.AuthenticationManager; +//////import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +//////import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +//////import org.springframework.security.config.annotation.web.builders.HttpSecurity; +//////import org.springframework.security.core.userdetails.UserDetailsService; +//////import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +//////import org.springframework.security.web.SecurityFilterChain; +//////import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +//////import org.springframework.web.servlet.config.annotation.CorsRegistry; +//////import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +////// +//////@RequiredArgsConstructor +//////@Configuration +//////public class WebSecurityConfig { +////// +////// private final UserDetailService userService; +////// +//////// @Bean //CORS 설정 +//////// public WebMvcConfigurer corsConfigurer() { +//////// return new WebMvcConfigurer() { +//////// @Override +//////// public void addCorsMappings(CorsRegistry registry) { +//////// registry.addMapping("/**") +//////// .allowedOrigins("*") +//////// .allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS") +//////// .allowedHeaders("*") +//////// .allowCredentials(true); +//////// } +//////// }; +//////// } +////// +////// @Bean +////// public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { +////// http +////// .authorizeHttpRequests(authorize -> authorize +////// .requestMatchers("/login", "/signup", "/user", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html","/user/**").permitAll() +////// .anyRequest().authenticated() +////// ) +////// .formLogin(form -> form +////// .loginPage("/login") // 기본 로그인 필요하면 위치 +////// .defaultSuccessUrl("/articles") //로그인에 성공하면 위치 +////// ) +////// .logout(logout -> logout +////// .logoutSuccessUrl("/login") //로그인에 성공하면 위치 +////// .invalidateHttpSession(true) //세션 만료 +////// ) +////// .csrf(csrf -> csrf.disable()); +////// +////// return http.build(); +////// } +////// +////// @Bean +////// public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { +////// return authenticationConfiguration.getAuthenticationManager(); +////// } +////// +////// @Bean +////// public BCryptPasswordEncoder bCryptPasswordEncoder() { +////// return new BCryptPasswordEncoder(); +////// } +//////} +//// +//// +//// +//////package com.todoslave.feedme.config.security; +//// +//// +////import com.todoslave.feedme.login.Service.CustomOAuth2UserService; +////import org.springframework.context.annotation.Bean; +////import org.springframework.context.annotation.Configuration; +////import org.springframework.security.authentication.AuthenticationManager; +////import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +////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.core.userdetails.UserDetailsService; +////import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +////import org.springframework.security.web.SecurityFilterChain; +////import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; +////import org.springframework.web.cors.CorsConfiguration; +////import org.springframework.web.cors.CorsConfigurationSource; +////import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +////import org.springframework.web.servlet.handler.HandlerMappingIntrospector; +//// +////import java.util.Collections; +//// +////@Configuration +////@EnableWebSecurity +////public class WebSecurityConfig { +//// +//// private final UserDetailsService userDetailsService; +//// +//// public WebSecurityConfig(UserDetailsService userDetailsService) { +//// this.userDetailsService = userDetailsService; +//// } +//// +//// @Bean +//// public BCryptPasswordEncoder passwordEncoder() { +//// return new BCryptPasswordEncoder(); +//// } +//// +//// @Bean +//// CorsConfigurationSource corsConfigurationSource() { +//// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); +//// CorsConfiguration config = new CorsConfiguration(); +//// config.setAllowedHeaders(Collections.singletonList("*")); +//// config.setAllowedMethods(Collections.singletonList("*")); +//// config.setAllowedOriginPatterns(Collections.singletonList("http://localhost:3000")); // 허용할 origin +//// config.setAllowCredentials(true); +//// source.registerCorsConfiguration("/**", config); +//// return source; +//// } +//// +////// @Bean +////// public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { +////// http +////// .httpBasic(httpBasicConfigurer -> httpBasicConfigurer.disable()) +////// .cors(corsConfigurer -> corsConfigurer.configurationSource(corsConfigurationSource())) +////// .csrf(AbstractHttpConfigurer::disable) +////// .authorizeHttpRequests(authorize -> +////// authorize +////// .requestMatchers("/login", "/signup", "/user", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/users/**").permitAll() +////// .anyRequest().authenticated() +////// ) +////// .formLogin(form -> form +////// .loginPage("/login") +////// .defaultSuccessUrl("/articles") +////// ) +////// .logout(logout -> logout +////// .logoutSuccessUrl("/login") +////// .invalidateHttpSession(true) +////// ); +////// +////// return http.build(); +////// } +//// +//// //되는거 +////// @Bean +////// public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector, CustomOAuth2UserService customOAuth2UserService) throws Exception { +////// http +////// +////// .csrf(AbstractHttpConfigurer::disable) +////// .sessionManagement((sessionManagement) -> +////// sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS) +////// ) +////// .formLogin(AbstractHttpConfigurer::disable) +////// .httpBasic(AbstractHttpConfigurer::disable) +////// .authorizeHttpRequests((authorizeRequests) -> authorizeRequests +////// .requestMatchers(new MvcRequestMatcher(introspector, "/api/user")).permitAll() +////// .anyRequest().authenticated() +////// ) +////// .oauth2Login(oauth2Login -> +////// oauth2Login.userInfoEndpoint(userInfoEndpointConfig -> +////// userInfoEndpointConfig.userService(customOAuth2UserService))) +////// .formLogin(form -> form +////// .loginPage("/login") // 기본 로그인 필요하면 위치 +////// +////// ) +////// ; +////// return http.build(); +////// } +//// @Bean +//// public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector, CustomOAuth2UserService customOAuth2UserService) throws Exception { +//// http +//// .httpBasic(httpBasicConfigurer -> httpBasicConfigurer.disable()) +//// .cors(corsConfigurer -> corsConfigurer.configurationSource(corsConfigurationSource())) +//// .csrf(AbstractHttpConfigurer::disable) +//// .authorizeHttpRequests(authorize -> +//// authorize +//// .requestMatchers("/login", "/signup", "/user", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/users/**").permitAll() +//// .requestMatchers(new MvcRequestMatcher(introspector, "/api/user")).permitAll() +//// .anyRequest().authenticated() +//// ) +//// .formLogin(form -> form +//// .loginPage("/login") +//// .defaultSuccessUrl("/login") +//// ) +//// .logout(logout -> logout +//// .logoutSuccessUrl("/login") +//// .invalidateHttpSession(true) +//// ) +//// .sessionManagement(sessionManagement -> +//// sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS) +//// ) +//// .oauth2Login(oauth2Login -> +//// oauth2Login.userInfoEndpoint(userInfoEndpointConfig -> +//// userInfoEndpointConfig.userService(customOAuth2UserService)) +//// ); +//// +//// return http.build(); +//// } +//// @Bean +//// public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { +//// return authenticationConfiguration.getAuthenticationManager(); +//// } +////} +//package com.todoslave.feedme.config.security; +// +//import com.todoslave.feedme.login.Service.CustomOAuth2UserService; +//import lombok.RequiredArgsConstructor; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.security.authentication.AuthenticationManager; +//import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +//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.configuration.WebSecurityCustomizer; +//import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +//import org.springframework.security.config.http.SessionCreationPolicy; +//import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +//import org.springframework.security.web.SecurityFilterChain; +//import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +//import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; +//import org.springframework.web.cors.CorsConfiguration; +//import org.springframework.web.cors.CorsConfigurationSource; +//import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +//import org.springframework.web.servlet.handler.HandlerMappingIntrospector; +// +//import java.util.Collections; +// +//@Configuration +//@EnableWebSecurity +//@RequiredArgsConstructor +//public class WebSecurityConfig { +// +// private final CustomOAuth2UserService customOAuth2UserService; +// +// +// @Bean //security를 적용할것 설절 +// public WebSecurityCustomizer webSecurityCustomizer() { +// return web -> web.ignoring() +// .requestMatchers("/error", "/favicon.ico"); +// } +// +// @Bean // 암호화 친구 +// public BCryptPasswordEncoder passwordEncoder() { +// return new BCryptPasswordEncoder(); +// } +// +// +// @Bean +// CorsConfigurationSource corsConfigurationSource() { // 의미 없고 +// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); +// CorsConfiguration config = new CorsConfiguration(); +// config.setAllowedHeaders(Collections.singletonList("*")); +// config.setAllowedMethods(Collections.singletonList("*")); +// config.setAllowedOriginPatterns(Collections.singletonList("http://localhost:3000")); // 허용할 origin +// config.setAllowCredentials(true); +// source.registerCorsConfiguration("/**", config); +// return source; +// } +// +// @Bean +// public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception { +// http +// .httpBasic(httpBasicConfigurer -> httpBasicConfigurer.disable()) // 이게 폼 +// .cors(corsConfigurer -> corsConfigurer.configurationSource(corsConfigurationSource())) //CORS 설정 +// .csrf(AbstractHttpConfigurer::disable) +// .authorizeHttpRequests(authorize -> +// authorize +// .requestMatchers("/login","/testsite" ,"/signup", "/user", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/users/**").permitAll() // 유저 설정 +// .requestMatchers(new MvcRequestMatcher(introspector, "/api/user")).permitAll() +// .anyRequest().authenticated() +// ) +// +// .oauth2Login(oauth2Login -> +// oauth2Login.userInfoEndpoint(userInfoEndpointConfig -> +// userInfoEndpointConfig.userService(customOAuth2UserService)) +// .loginPage("/login") // 소셜 로그인 페이지 +// .successHandler(new SimpleUrlAuthenticationSuccessHandler("/templates/testsite.html")) // 로그인 성공 시 리디렉션 URL +// ) +// .logout(logout -> logout +// .logoutUrl("/logout") +// .logoutSuccessUrl("/login") +// .invalidateHttpSession(true) +// .deleteCookies("JSESSIONID") +// .permitAll() +// ) +// .sessionManagement(sessionManagement -> +// sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS) +// ); +// +// return http.build(); +// } +// +// @Bean +// public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { +// return authenticationConfiguration.getAuthenticationManager(); +// } +//} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/filter/JwtAuthFilter.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/filter/JwtAuthFilter.java new file mode 100644 index 0000000..95b9e1b --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/filter/JwtAuthFilter.java @@ -0,0 +1,210 @@ +package com.todoslave.feedme.config.security.filter; + + +import com.todoslave.feedme.login.dto.RefreshToken; +import com.todoslave.feedme.login.dto.TokenResponseStatus; +import com.todoslave.feedme.login.util.SecurityUserDto; +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.login.Handler.JWTUtill; +import com.todoslave.feedme.repository.MemberRepository; +import com.todoslave.feedme.repository.RefreshTokenRepository; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.JwtException; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +@RequiredArgsConstructor +@Slf4j +@Component +public class JwtAuthFilter extends OncePerRequestFilter { + + private final JWTUtill jwtUtil; + private final MemberRepository memberRepository; + private final RefreshTokenRepository tokenRepository; + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + return request.getRequestURI().contains("token/refresh"); + } + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // request Header에서 AccessToken을 가져온다. + String atc = request.getHeader("Authorization"); + + // 토큰 검사 생략(모두 허용 URL의 경우 토큰 검사 통과) + if (!StringUtils.hasText(atc)) { + doFilter(request, response, filterChain); + return; + } + + // AccessToken을 검증하고, 만료되었을경우 예외를 발생시킨다. + if (!jwtUtil.verifyToken(atc)) { + + Optional refreshToken = tokenRepository.findByAccessToken(atc); + System.out.println("바보"); + System.out.println(refreshToken); + + if (refreshToken.isPresent() && jwtUtil.verifyToken(refreshToken.get().getRefreshToken())) { + + System.out.println("바봉"); + + // RefreshToken 객체를 꺼내온다. + RefreshToken resultToken = refreshToken.get(); + // 권한과 아이디를 추출해 새로운 액세스토큰을 만든다. + String newAccessToken = jwtUtil.generateAccessToken(resultToken.getId(), jwtUtil.getRole(resultToken.getRefreshToken())); + // 액세스 토큰의 값을 수정해준다. + resultToken.updateAccessToken(newAccessToken); + tokenRepository.save(resultToken); + + // 새로운 액세스 토큰을 헤더에 추가 + response.setHeader("Authorization", "Bearer " + newAccessToken); + + // 기존 요청을 업데이트된 토큰과 함께 다시 처리 + request.setAttribute("Authorization", "Bearer " + newAccessToken); + SecurityContextHolder.getContext().setAuthentication(getAuthentication(SecurityUserDto.builder() + .id(Integer.valueOf(resultToken.getId())) + .email(jwtUtil.getUid(resultToken.getAccessToken())) + .role(jwtUtil.getRole(resultToken.getAccessToken())) + .build())); + + // 기존 필터 체인을 다시 호출 + filterChain.doFilter(request, response); + + } else{ + throw new JwtException("Refresh 토큰과 Access Token 모두 만료되었습니다."); // 로그아웃 처리 + } + } + + // AccessToken의 값이 있고, 유효한 경우에 진행한다. + if (jwtUtil.verifyToken(atc)) { + + // AccessToken 내부의 payload에 있는 email로 user를 조회한다. 없다면 예외를 발생시킨다 -> 정상 케이스가 아님 + Member findMember = memberRepository.findByEmail(jwtUtil.getUid(atc)) + .orElseThrow(IllegalStateException::new); + + // SecurityContext에 등록할 User 객체를 만들어준다. + SecurityUserDto userDto = SecurityUserDto.builder() + .id(findMember.getId()) + .email(findMember.getEmail()) + .role("ROLE_".concat(findMember.getUserRole())) + .nickname(findMember.getNickname()) + .build(); + + // SecurityContext에 인증 객체를 등록해준다. + Authentication auth = getAuthentication(userDto); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + filterChain.doFilter(request, response); + } + + + + public Authentication getAuthentication(SecurityUserDto member) { + return new UsernamePasswordAuthenticationToken(member, "", + List.of(new SimpleGrantedAuthority(member.getRole()))); + } + +} + +//package com.todoslave.feedme.config.security.filter; +// +// +//import com.todoslave.feedme.login.util.SecurityUserDto; +//import com.todoslave.feedme.domain.entity.membership.Member; +//import com.todoslave.feedme.login.Handler.JWTUtill; +//import com.todoslave.feedme.repository.MemberRepository; +//import jakarta.servlet.FilterChain; +//import jakarta.servlet.ServletException; +//import jakarta.servlet.http.HttpServletRequest; +//import jakarta.servlet.http.HttpServletResponse; +//import lombok.RequiredArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +//import org.springframework.security.core.Authentication; +//import org.springframework.security.core.authority.SimpleGrantedAuthority; +//import org.springframework.security.core.context.SecurityContextHolder; +//import org.springframework.security.oauth2.jwt.JwtException; +//import org.springframework.stereotype.Component; +//import org.springframework.util.StringUtils; +//import org.springframework.web.filter.OncePerRequestFilter; +// +//import java.io.IOException; +//import java.util.List; +// +//@RequiredArgsConstructor +//@Slf4j +//@Component +//public class JwtAuthFilter extends OncePerRequestFilter { +// +// private final JWTUtill jwtUtil; +// private final MemberRepository memberRepository; +// +// @Override +// protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { +// return request.getRequestURI().contains("token/refresh"); +// } +// @Override +// protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { +// // request Header에서 AccessToken을 가져온다. +// String atc = request.getHeader("Authorization"); +// +// // 토큰 검사 생략(모두 허용 URL의 경우 토큰 검사 통과) +// if (!StringUtils.hasText(atc)) { +// doFilter(request, response, filterChain); +// return; +// } +// +// // AccessToken을 검증하고, 만료되었을경우 예외를 발생시킨다. +// if (!jwtUtil.verifyToken(atc)) { +// throw new JwtException("Access Token 만료!"); +// +// +// } +// +// // AccessToken의 값이 있고, 유효한 경우에 진행한다. +// if (jwtUtil.verifyToken(atc)) { +// +// // AccessToken 내부의 payload에 있는 email로 user를 조회한다. 없다면 예외를 발생시킨다 -> 정상 케이스가 아님 +// Member findMember = memberRepository.findByEmail(jwtUtil.getUid(atc)) +// .orElseThrow(IllegalStateException::new); +// +// // SecurityContext에 등록할 User 객체를 만들어준다. +// SecurityUserDto userDto = SecurityUserDto.builder() +// .id(findMember.getId()) +// .email(findMember.getEmail()) +// .role("ROLE_".concat(findMember.getUserRole())) +// .nickname(findMember.getNickname()) +// .build(); +// +// // SecurityContext에 인증 객체를 등록해준다. +// Authentication auth = getAuthentication(userDto); +// SecurityContextHolder.getContext().setAuthentication(auth); +// } +// +// filterChain.doFilter(request, response); +// } +// +// +// +// public Authentication getAuthentication(SecurityUserDto member) { +// return new UsernamePasswordAuthenticationToken(member, "", +// List.of(new SimpleGrantedAuthority(member.getRole()))); +// } +// +//} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/filter/JwtExceptionFilter.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/filter/JwtExceptionFilter.java new file mode 100644 index 0000000..29d2007 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/config/security/filter/JwtExceptionFilter.java @@ -0,0 +1,36 @@ +package com.todoslave.feedme.config.security.filter; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.todoslave.feedme.config.security.StatusResponseDto; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.security.oauth2.jwt.JwtException; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class JwtExceptionFilter extends OncePerRequestFilter { + + private final ObjectMapper objectMapper; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + try { + filterChain.doFilter(request, response); + } catch (JwtException e) { + response.setStatus(401); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding("UTF-8"); + objectMapper.writeValue(response.getWriter(), StatusResponseDto.addStatus(401)); + } + } +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/AlarmController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/AlarmController.java new file mode 100644 index 0000000..8c7fd06 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/AlarmController.java @@ -0,0 +1,65 @@ +package com.todoslave.feedme.controller; + +import com.todoslave.feedme.DTO.AlarmCheckRequestDTO; +import com.todoslave.feedme.login.util.SecurityUtil; +import com.todoslave.feedme.service.AlarmService; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/alarms") +public class AlarmController { + + private final SecurityUtil securityUtil; + private AlarmService alarmService; + + private final Map emitters = new ConcurrentHashMap<>(); + + @GetMapping(value = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public SseEmitter subscribe(){ + + //시큐리티 이상함 유틸 주석 + int memberId = securityUtil.getCurrentUserId(); + + SseEmitter emitter = new SseEmitter(); + emitters.put(memberId, emitter); + emitter.onCompletion(()->emitters.remove(memberId)); + emitter.onTimeout(()->emitters.remove(memberId)); + return emitter; + + } + +// private void sendAlarmToMember(String message) throws Exception{ +// +// int memberId = securityUtil.getId(); +// +// SseEmitter emitter = emitters.get(memberId); +// if(emitter != null){ +// emitter.send(SseEmitter.event().name("Alarm").data(message)); +// } +// +// } + + //DTO 없음 주석 +// @RequestMapping("/read") +// private void checkAlarm(@RequestBody AlarmCheckRequestDTO alarmCheckRequestDTO){ +// +// int memberId = securityUtil.getId(); +// +// LocalDateTime checkTime = LocalDateTime.parse(alarmCheckRequestDTO.getCheckTime(), DateTimeFormatter.ISO_DATE_TIME); +// AlarmCheckServiceDTO alarmCheckServiceDTO = new AlarmCheckServiceDTO(); +// alarmCheckServiceDTO.setCheckTime(checkTime); +// +// +// } + + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/AuthController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/AuthController.java new file mode 100644 index 0000000..eb26abf --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/AuthController.java @@ -0,0 +1,67 @@ +package com.todoslave.feedme.controller; + +import com.todoslave.feedme.config.security.StatusResponseDto; +import com.todoslave.feedme.login.Handler.JWTUtill; +import com.todoslave.feedme.login.dto.RefreshToken; +import com.todoslave.feedme.login.dto.TokenResponseStatus; +import com.todoslave.feedme.repository.RefreshTokenRepository; +import com.todoslave.feedme.login.Service.RefreshTokenService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Optional; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class AuthController { + + private final RefreshTokenRepository tokenRepository; + private final RefreshTokenService tokenService; + private final JWTUtill jwtUtil; + + @PostMapping("token/logout") + public ResponseEntity logout(@RequestHeader("Authorization") final String accessToken) { + // 엑세스 토큰으로 현재 Redis 정보 삭제 + tokenService.removeRefreshToken(accessToken); + + return ResponseEntity.ok(StatusResponseDto.addStatus(200)); + } + + @PostMapping("/token/refresh") + public ResponseEntity refresh(@RequestHeader("Authorization") final String accessToken) { + + // 액세스 토큰으로 Refresh 토큰 객체를 조회 + Optional refreshToken = tokenRepository.findByAccessToken(accessToken); + + System.out.println(refreshToken.get().getRefreshToken()); + + + // RefreshToken이 존재하고 유효하다면 실행 + if (refreshToken.isPresent() && jwtUtil.verifyToken(refreshToken.get().getRefreshToken())) { + + System.out.println("바봉"); + + // RefreshToken 객체를 꺼내온다. + RefreshToken resultToken = refreshToken.get(); + // 권한과 아이디를 추출해 새로운 액세스토큰을 만든다. + String newAccessToken = jwtUtil.generateAccessToken(resultToken.getId(), jwtUtil.getRole(resultToken.getRefreshToken())); + // 액세스 토큰의 값을 수정해준다. + resultToken.updateAccessToken(newAccessToken); + tokenRepository.save(resultToken); + System.out.println(); + System.out.println(accessToken); + System.out.println(resultToken); + + // 새로운 액세스 토큰을 반환해준다. + return ResponseEntity.ok(TokenResponseStatus.addStatus(200, newAccessToken)); + } + + return ResponseEntity.badRequest().body(TokenResponseStatus.addStatus(400, null)); + } + +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/CreatureController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/CreatureController.java new file mode 100644 index 0000000..68ca7b2 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/CreatureController.java @@ -0,0 +1,53 @@ +package com.todoslave.feedme.controller; + +import com.todoslave.feedme.DTO.CreatureInfoResponseDTO; +import com.todoslave.feedme.DTO.CreatureMakeRequestDTO; +import com.todoslave.feedme.domain.entity.avatar.Creature; +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.login.util.SecurityUtil; +import com.todoslave.feedme.service.CreatureService; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@RestController +@RequestMapping("/creature") +@RequiredArgsConstructor +public class CreatureController { + + private final CreatureService creatureService; + + + @Operation(summary = "크리쳐 생성") + @PostMapping + public ResponseEntity createCreature(@RequestBody CreatureMakeRequestDTO request, @RequestHeader("Authorization") final String accessToken) { + Member member = SecurityUtil.getCurrentMember(); + + Creature creature = creatureService.createFristCreature(request.getKeyword(), request.getPhoto(), request.getCreatureName()); + + return ResponseEntity.ok(Map.of("creatureId", creature.getId(), "message", "크리쳐가 성공적으로 생성되었습니다.")); + } + + @Operation(summary = "크리쳐 보기") + @GetMapping + public ResponseEntity getCreatures(@RequestHeader("Authorization") final String accessToken) { + CreatureInfoResponseDTO creature = creatureService.CreatureInfo(); + + return new ResponseEntity(creature, HttpStatus.OK); + } + + @Operation(summary = "크리쳐 삭제") + @DeleteMapping + public ResponseEntity delectCreature(@RequestHeader("Authorization") final String accessToken) { + if(creatureService.removeCreature()){ + return new ResponseEntity("삭제되었습니다.", HttpStatus.OK);}else{ + return new ResponseEntity("해당 크리쳐를 찾을 수 없습니다.", HttpStatus.BAD_REQUEST); + } + } + + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/CreatureTodoController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/CreatureTodoController.java new file mode 100644 index 0000000..e0bb5da --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/CreatureTodoController.java @@ -0,0 +1,62 @@ +package com.todoslave.feedme.controller; + +import com.todoslave.feedme.DTO.*; +import com.todoslave.feedme.service.CreatureTodoService; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +@RestController +@RequestMapping("/creatureTodo") +@RequiredArgsConstructor +public class CreatureTodoController { + + final private CreatureTodoService creatureTodoService; + + // 크리쳐 투두 생성 + @Operation(summary = "크리쳐 투두 생성 / 있으면 생성 X") + @GetMapping("/{weather}") + public ResponseEntity> createTodo(@PathVariable String weather) { + System.out.println("이거 맞아?"); + List list = new ArrayList<>(); + list = creatureTodoService.insertTodo(weather); + return ResponseEntity.ok(list); + } + + // 투두 완료 + @PostMapping("/complete/{id}") + @Operation(summary = "투두 일정 하나 완료/취소") + public ResponseEntity completeTodo(@PathVariable int id){ + return ResponseEntity.ok(creatureTodoService.completeTodo(id)); + } + + // 오늘 완료되지 않은 투두 목록 가져오기 + @GetMapping("/main/daily") + @Operation(summary = "오늘 완료되지 않은 크리쳐 투두 목록 가져오기") + public ResponseEntity> getCreatureTodoMainDaily() { + List list = creatureTodoService.getCreatureTodoMainDaily(); + return ResponseEntity.ok(list); + } + + // 메인 달력에서 일정 불러오기 + @GetMapping("/calendar/daily") + @Operation(summary = "해당일의 크리쳐 투두 목록 가져오기") + public ResponseEntity> findCalendarCreatureTodoList(@RequestBody CretureTodoRequestDTO cretureTodoRequestDTO) { + List list = creatureTodoService.getCreatureTodoCalendarDaily(cretureTodoRequestDTO); + return ResponseEntity.ok(list); + } + + // 할일 목록에서 일정(일) 불러오기 + @GetMapping("/todolist/daily") + public ResponseEntity> findDailyCreatureTodoList(@RequestBody CreatureTodoDailyRequestDTO creatureTodoDailyRequestDTO){ + return ResponseEntity.ok(creatureTodoService.getCreatureTodoListDaily(creatureTodoDailyRequestDTO)); + } + + + +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FeedCommentController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FeedCommentController.java new file mode 100644 index 0000000..caa1809 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FeedCommentController.java @@ -0,0 +1,52 @@ +package com.todoslave.feedme.controller; + +import com.todoslave.feedme.DTO.*; +import com.todoslave.feedme.domain.entity.Feed.FeedComment; +import com.todoslave.feedme.service.FeedCommentService; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpSession; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/feedComment") +@RequiredArgsConstructor +public class FeedCommentController { + + private final FeedCommentService feedCommentService; + + // 피드 생성 + @Operation(summary = "댓글 생성") + @PostMapping("/{feed_id}") + public ResponseEntity registComment(@PathVariable("feed_id") int feedId, @RequestBody FeedCommentRequestDTO feedCommentRequestDTO) { + return ResponseEntity.ok(feedCommentService.insertFeedCommend(feedCommentRequestDTO,feedId)); + } + + // 피드 수정 + @Operation(summary = "댓글 수정") + @PatchMapping("/{feedComment_Id}") + public ResponseEntity modifyComment(@PathVariable("feedComment_Id") int feedcommantId, @RequestBody FeedCommentRequestDTO feedCommentRequestDTO) { + FeedCommentResponseDTO dto = feedCommentService.modifyComment(feedCommentRequestDTO, feedcommantId); + if (dto == null) { + String msg = "내가 쓴 글이 아니거나, 삭제된 글 입니다."; + return new ResponseEntity(msg, HttpStatus.BAD_REQUEST); + } + return ResponseEntity.ok(dto); + } + + // 피드 삭제 + @Operation(summary = "댓글 삭제") + @DeleteMapping("/{feedComment_Id}") + public ResponseEntity delectFeedComment(@PathVariable int feedComment_Id) { + boolean succes = feedCommentService.delectFeedComment(feedComment_Id); + if (succes) { + String msg = "삭제되었습니다"; + return new ResponseEntity(msg, HttpStatus.OK); + } + String msg = "내가 쓴 글이 아니거나, 삭제된 글 입니다."; + return new ResponseEntity(msg, HttpStatus.BAD_REQUEST); + } + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FeedController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FeedController.java index f98a4ca..b64d638 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FeedController.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FeedController.java @@ -1,5 +1,76 @@ package com.todoslave.feedme.controller; +import com.todoslave.feedme.DTO.CreatureTodoResponseDTO; +import com.todoslave.feedme.DTO.FeedModifyRequest; +import com.todoslave.feedme.DTO.FeedRequestDTO; +import com.todoslave.feedme.DTO.FeedResponseDTO; +import com.todoslave.feedme.service.FeedService; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + +@RestController +@RequestMapping("/feed") +@RequiredArgsConstructor public class FeedController { + private final FeedService feedService; + + // 피드 생성 + @Operation(summary = "피드 생성") + @PostMapping + public ResponseEntity createFeed(@RequestBody FeedRequestDTO feedRequestDTO) { + FeedResponseDTO dto = feedService.insertFeed(feedRequestDTO); + return ResponseEntity.ok(dto); + } + + // 피드 수정 + @Operation(summary = "피드 수정") + @PatchMapping("/{feedId}") + public ResponseEntity modifyFeed(@PathVariable int feedId, @RequestBody FeedModifyRequest feedModifyRequest) { + FeedResponseDTO dto = feedService.modifyFeed(feedId ,feedModifyRequest); + return ResponseEntity.ok(dto); + } + + // 피드 삭제 + @Operation(summary = "피드 삭제") + @DeleteMapping("/{feedId}") + public ResponseEntity delectFeed(@PathVariable int feedId) { + if(feedService.delectFeed(feedId)){ + return new ResponseEntity("삭제되었습니다.", HttpStatus.OK); + }else{ + return new ResponseEntity("본인글만 지우세요", HttpStatus.BAD_REQUEST); + } + } + + // 피드 보기 + @Operation(summary = "피드 보기") + @GetMapping + public ResponseEntity findFeed(@PathVariable int feedId) { + //친구 찾기 + + // + + + return null; + } + + +// +// // 크리쳐 투두 생성 +// @Operation(summary = "크리쳐 투두 생성 / 있으면 생성 X") +// @GetMapping("/{weather}") +// public ResponseEntity> createTodo(@PathVariable String weather) { +// System.out.println("이거 맞아?"); +// List list = new ArrayList<>(); +// list = creatureTodoService.insertTodo(weather); +// return ResponseEntity.ok(list); +// } + + } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FeedReCommentController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FeedReCommentController.java new file mode 100644 index 0000000..14a73df --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FeedReCommentController.java @@ -0,0 +1,12 @@ +package com.todoslave.feedme.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/feedReComment") +@RequiredArgsConstructor +public class FeedReCommentController { + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FriendController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FriendController.java index 22e39de..71f69e0 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FriendController.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/FriendController.java @@ -1,89 +1,68 @@ -//package com.todoslave.feedme.controller; -// -//import com.todoslave.feedme.domain.entity.communication.Friend; -//import com.todoslave.feedme.domain.entity.communication.FriendRequest; -//import com.todoslave.feedme.service.FriendService; -//import lombok.RequiredArgsConstructor; -//import org.springframework.http.RequestEntity; -//import org.springframework.http.ResponseEntity; -//import org.springframework.web.bind.annotation.*; -// -//import java.util.List; -// -//@RestController -//@RequiredArgsConstructor -//@RequestMapping("/friends") -//public class FriendController { -// -// static private FriendService friendService; -// -// // 친구 추가하기 -// @PostMapping -// public ResponseEntity addFriend(@RequestParam("token") String token, @RequestParam("counterEmail") String email){ -// -// // token으로부터 memberId를 찾는 로직 -// int memberId; -// -// friendService.insertFriend(memberId, email); -// -// return ResponseEntity.noContent().build(); -// } -// -// // 친구 삭제하기 -// @DeleteMapping("/{id}") -// public ResponseEntity removeFriend(@RequestHeader("Authorization") String token, @RequestParam("id") String friendId){ -// -// // token으로부터 memberId를 찾는 로직 -// int memberId; -// -// friendService.deleteFriend(friendId); -// -// return ResponseEntity.noContent().build(); -// } -// -// // 친구 목록 불러오기 -// @GetMapping -// public ResponseEntity> findFriends(@RequestHeader("Authorization")String token){ -// //token으로부터 memberId를 찾는 로직 -// int memberId; -// -// return ResponseEntity.ok(friendService.getFriends(memberId)); -// } -// -// // 친구 요청 목록 조회 -// @GetMapping("/request") -// public ResponseEntity> findRequestFriend(@RequestHeader("Authorization") String token){ -// //token으로부터 memberId를 찾는 로직 -// int memberId; -// -// return ResponseEntity.ok(friendService.getRequestFriend(memberId)); -// } -// -// // 친구 수락하기 -// @PostMapping("/{id}/accept") -// public ResponseEntity acceptFriendship(@RequestHeader("Authorization") String token, @PathVariable("id") int requestId){ -// // token 인증 -// if(true) { -// friendService.insertFriendship(requestId); -// } -// -// return ResponseEntity.noContent().build(); -// } -// -// // 친구 거절하기 -// @PostMapping("/{id}/reject") -// public ResponseEntity rejectFriendship(@RequestHeader("Authorization") String token, @PathVariable("id") int requestId){ -// // token 인증 -// if(true){ -// friendService.deleteRequestFriend(requestId); -// } -// -// return ResponseEntity.noContent().build(); -// } -// -// // 친구 활동 피드 -// -// // 친구 일정 공유 -// -// -//} +package com.todoslave.feedme.controller; + +import com.todoslave.feedme.DTO.FriendReqRequestDTO; +import com.todoslave.feedme.DTO.FriendReqResponseDTO; +import com.todoslave.feedme.DTO.FriendResponseDTO; +import com.todoslave.feedme.domain.entity.communication.Friend; +import com.todoslave.feedme.domain.entity.communication.FriendRequest; +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.service.AlarmService; +import com.todoslave.feedme.service.FriendService; +import com.todoslave.feedme.service.MemberService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/friends") +public class FriendController { + + static private MemberService memberService; + static private FriendService friendService; + static private AlarmService alarmService; + + // 친구 요청하기 + @PostMapping + public ResponseEntity addFriend(FriendReqRequestDTO friendReqRequestDTO){ + friendService.requestFriend(friendReqRequestDTO); + return ResponseEntity.noContent().build(); + } + + // 친구 삭제하기 + @DeleteMapping("/{id}") + public ResponseEntity removeFriend(@RequestParam("id") int friendId){ + friendService.deleteFriend(friendId); + return ResponseEntity.noContent().build(); + } + + // 친구 목록 불러오기 + @GetMapping + public ResponseEntity> findFriends(){ + return ResponseEntity.ok(friendService.getFriends()); + } + + // 친구 요청 목록 조회 + @GetMapping("/request") + public ResponseEntity> findRequestFriend(){ + return ResponseEntity.ok(friendService.getRequestFriend()); + } + + // 친구 요청 수락하기 + @PostMapping("/accept/{id}") + public ResponseEntity acceptFriendship(@PathVariable("id") int requestId){ + friendService.insertFriendship(requestId); + return ResponseEntity.noContent().build(); + } + + // 친구 요청 거절하기 + @PostMapping("/reject/{id}") + public ResponseEntity rejectFriendship(@PathVariable("id") int requestId){ + friendService.deleteRequestFriend(requestId); + return ResponseEntity.noContent().build(); + } + + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/MemberChatController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/MemberChatController.java index a48e0dc..fd4fa2d 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/MemberChatController.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/MemberChatController.java @@ -1,95 +1,102 @@ -package com.todoslave.feedme.controller; - -import com.todoslave.feedme.DTO.PaginationRequest; -import com.todoslave.feedme.domain.entity.communication.MemberChatMessage; -import com.todoslave.feedme.domain.entity.communication.MemberChatRoom; -import com.todoslave.feedme.service.MemberChatService; -import java.util.ArrayList; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Slice; -import org.springframework.http.ResponseEntity; -import org.springframework.messaging.handler.annotation.DestinationVariable; -import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.handler.annotation.Payload; -import org.springframework.messaging.handler.annotation.SendTo; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@RequiredArgsConstructor -@RestController -@RequestMapping("/friends/chats") -public class MemberChatController { - - @Autowired - private final MemberChatService chatService; - - // 이 유저가 누구랑 채팅방을 갖고 있는 지 찾기 - @GetMapping - public ResponseEntity> findChatRoomList(@RequestHeader("Authorization") String token){ - - MemberChatRoom room = new MemberChatRoom(); - List members = new ArrayList<>(); - - // jwt 토큰에서 id 얻어오기 - String memberId = "-1"; - - members.add(memberId); - room.setParticipantIds(members); - - return ResponseEntity.ok(chatService.getChatRooms(room)); - } - - // 채팅방 ID 찾기 - @PostMapping - public ResponseEntity findChatRoom(@RequestHeader("Authorization") String token, - @RequestParam("counterpartId") String counterpartId) { - - String memberId = "-1"; - - if ( memberId == null || counterpartId == null) { - return ResponseEntity.badRequest().build(); - } - - MemberChatRoom room = new MemberChatRoom(); - - List members = new ArrayList<>(); - - members.add(memberId); - members.add(counterpartId); - - room.setParticipantIds(members); - - return ResponseEntity.ok(chatService.getChatRoom(room)); - - } - - // 메세지 불러오기 - @MessageMapping("/loadMessages/{roomId}") - @SendTo("/chatRoom/loadMessages/{roomId}") - public Slice findMessages(@DestinationVariable String roomId, - @Payload PaginationRequest request){ - System.out.println("receive message?"); - Slice messages = chatService.getChatMessage(roomId, request.getSkip(), request.getLimit()); - - for (MemberChatMessage message : messages) { - System.out.println(message.toString()); - } - - return messages; - } - - // 메세지 저장 - @MessageMapping("/messages/{roomId}") - @SendTo("/chatRoom/messages/{roomId}") - public MemberChatMessage sendMessage(@DestinationVariable String roomId, @Payload MemberChatMessage memberChatMessage) { - memberChatMessage.setMemberChatRoomId(roomId); - return chatService.insertChatMessage(memberChatMessage); - } -} +//맴버 채팅 주석 + +//package com.todoslave.feedme.controller; +// +//import com.todoslave.feedme.DTO.ChatFriendCreateRequestDTO; +//import com.todoslave.feedme.DTO.ChatFriendFindDTO; +//import com.todoslave.feedme.DTO.ChatMessageRequestDTO; +//import com.todoslave.feedme.DTO.PaginationRequestDTO; +//import com.todoslave.feedme.domain.entity.communication.MemberChatMessage; +//import com.todoslave.feedme.domain.entity.communication.MemberChatRoom; +//import com.todoslave.feedme.service.MemberChatService; +//import com.todoslave.feedme.service.MemberService; +//import java.util.ArrayList; +//import java.util.List; +//import lombok.RequiredArgsConstructor; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.data.domain.Slice; +//import org.springframework.http.ResponseEntity; +//import org.springframework.messaging.handler.annotation.DestinationVariable; +//import org.springframework.messaging.handler.annotation.MessageMapping; +//import org.springframework.messaging.handler.annotation.Payload; +//import org.springframework.messaging.handler.annotation.SendTo; +//import org.springframework.web.bind.annotation.GetMapping; +//import org.springframework.web.bind.annotation.PostMapping; +//import org.springframework.web.bind.annotation.RequestBody; +//import org.springframework.web.bind.annotation.RequestHeader; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RequestParam; +//import org.springframework.web.bind.annotation.RestController; +// +//@RequiredArgsConstructor +//@RestController +//@RequestMapping("/friends/chats") +//public class MemberChatController { +// +// private final MemberChatService chatService; +// private final MemberService memberService; +// +// // 이 유저가 누구랑 채팅방을 갖고 있는 지 찾기 +// @GetMapping +// public ResponseEntity> findChatRoomList(){ +// +// ChatFriendFindDTO chatFriendFindDTO = new ChatFriendFindDTO(); +// +// // jwt 토큰에서 id 얻어오기 +// int memberId = -1; +// +// List members = new ArrayList<>(); +// members.add(memberId); +// +// chatFriendFindDTO.setMemberId(members); +// +// return ResponseEntity.ok(chatService.getChatRooms(chatFriendFindDTO)); +// } +// +// // 채팅방 생성 +// @PostMapping +// public ResponseEntity createChatRoom( +// @RequestBody ChatFriendCreateRequestDTO chatFriendCreateRequestDTO){ +// +// // 토큰으로 받아오기 +// int memberId; +//// int counterpartId = memberService.(대충닉네임으로멤버아이디찾는메서드); +// +// if(counterpartId == -1){ +// return ResponseEntity.badRequest().build(); +// } +// +// List members = new ArrayList<>(); +// +// try { +// members.add(memberId); +// members.add(counterpartId); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// +// +// } +// +// // 메세지 불러오기 +// @MessageMapping("/loadMessages/{roomId}") +// @SendTo("/chatRoom/loadMessages/{roomId}") +// public Slice findMessages(@DestinationVariable String roomId, +// @Payload PaginationRequestDTO request){ +// System.out.println("receive message?"); +// Slice messages = chatService.getChatMessage(roomId, request.getSkip(), request.getLimit()); +// +// for (MemberChatMessage message : messages) { +// System.out.println(message.toString()); +// } +// +// return messages; +// } +// +// // 메세지 저장 +// @MessageMapping("/messages/{roomId}") +// @SendTo("/chatRoom/messages/{roomId}") +// public MemberChatMessage sendMessage(@DestinationVariable String roomId, @Payload ChatMessageRequestDTO chatMessageRequestDTO) { +// return chatService.insertChatMessage(roomId, chatMessageRequestDTO); +// } +//} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/MemberController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/MemberController.java new file mode 100644 index 0000000..05428d2 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/MemberController.java @@ -0,0 +1,134 @@ +package com.todoslave.feedme.controller; + +import com.todoslave.feedme.DTO.MemberSearchResponseDTO; +import com.todoslave.feedme.DTO.MemberSignupRequestDTO; +import com.todoslave.feedme.DTO.MypageResponseDTO; +import com.todoslave.feedme.config.jwt.JwtProperties; + +import com.todoslave.feedme.domain.entity.membership.Member; + +import com.todoslave.feedme.login.Handler.JWTUtill; +import com.todoslave.feedme.login.Service.RefreshTokenService; +import com.todoslave.feedme.login.Service.TokenBlacklistService; +import com.todoslave.feedme.login.dto.TokenResponseStatus; +import com.todoslave.feedme.login.util.SecurityUtil; +import com.todoslave.feedme.repository.MemberRepository; +import com.todoslave.feedme.service.MemberService; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.web.bind.annotation.*; + +import java.util.Base64; +import java.util.Date; +import java.util.List; + +@RestController +@RequestMapping("/users") +@Tag(name = "회원 CRUD") +@RequiredArgsConstructor +public class MemberController { + + private final JwtProperties jwtProperties; + private final MemberService memberService; + private final TokenBlacklistService tokenBlacklistService; + private final RefreshTokenService tokenService; + private String secretKey; + + @PostConstruct + protected void init() { + secretKey = Base64.getEncoder().encodeToString(jwtProperties.getSecretKey().getBytes()); + } + + + @Operation(summary = "맴버 검색 (for 친구추가)") + @GetMapping("/{searchvalue}") + public ResponseEntity> getUsers(@PathVariable String searchvalue) { + List list = memberService.getMemberList(searchvalue); + return new ResponseEntity>(list, HttpStatus.OK); + } + + @Operation(summary = "맴버 가입") + @PostMapping + public ResponseEntity SignupMember(@RequestBody MemberSignupRequestDTO memberSignupRequestDTO){ + + Member member = memberService.registerMember(memberSignupRequestDTO); + return ResponseEntity.ok(member); + } + + @Operation(summary = "맴버 수정") + @PatchMapping + public ResponseEntity UpdateMember(@RequestBody MemberSignupRequestDTO memberSignupRequestDTO){ + + Member member = memberService.updateMember(memberSignupRequestDTO); + + return ResponseEntity.ok(member); + } + + @Operation(summary = "맴버 정보 가져오기 (테스트용)") + @GetMapping("/holder/test") + public Member getCurrentMember() { + return SecurityUtil.getCurrentMember(); + } + + + + @Operation(summary = "내 정보 페이지") + @GetMapping("/mypage") + public ResponseEntity mypage(@RequestHeader("Authorization") final String accessToken) { +// MypageResponseDTO dto = memberService.getMyPage(); + + return ResponseEntity.ok(memberService.getMyPage()); + } + + @Operation(summary = "로그아웃") + @GetMapping("/logout") + public ResponseEntity logout(@RequestHeader("Authorization") final String accessToken) { + + // 토큰을 블랙리스트에 추가하여 무효화 + Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(accessToken).getBody(); + Date expiryDate = claims.getExpiration(); + + // 블랙리스트 서비스에 토큰을 추가 + tokenBlacklistService.addToBlacklist(accessToken, expiryDate); + + //레디스에서 제거 + tokenService.removeRefreshToken(accessToken); + + return new ResponseEntity<>("Logged out successfully", HttpStatus.OK); + } + + + @Operation(summary = "맴버 탈퇴") + @DeleteMapping + public ResponseEntity deleteMember(@RequestHeader("Authorization") final String accessToken) { + int id = SecurityUtil.getCurrentUserId(); + + if (id == -1) { + return ResponseEntity.badRequest().body("로그인 상태를 확인해주세요."); + } + + boolean isDeleted = memberService.removeMember(); // id를 인자로 전달하도록 수정 + + if (isDeleted) { + return ResponseEntity.ok("정상적으로 삭제되었습니다."); + } else { + return ResponseEntity.status(HttpStatus.CONFLICT).body("이미 삭제된 회원이거나, 로그인 상태를 확인해주세요."); + } + } + + + + + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/TodoCategoryController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/TodoCategoryController.java new file mode 100644 index 0000000..e0225bb --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/TodoCategoryController.java @@ -0,0 +1,50 @@ +package com.todoslave.feedme.controller; + +import com.todoslave.feedme.DTO.TodoCategoryRequestDTO; +import com.todoslave.feedme.DTO.TodoCategoryResponseDTO; +import com.todoslave.feedme.domain.entity.task.TodoCategory; +import com.todoslave.feedme.service.TodoCategoryService; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/category") +public class TodoCategoryController { + + private TodoCategoryService todoCategoryService; + + @GetMapping + public ResponseEntity> findTodoCategories(){ + return ResponseEntity.ok(todoCategoryService.getCategories()); + } + + @PostMapping("/{name}") + public ResponseEntity createTodoCategory(@PathVariable String name){ + return ResponseEntity.ok(todoCategoryService.insertCategory(name)); + } + + @DeleteMapping + public ResponseEntity removeTodoCategory(@RequestBody TodoCategoryRequestDTO category){ + todoCategoryService.deleteCategory(category.getId()); + return ResponseEntity.noContent().build(); + } + + @PatchMapping + public ResponseEntity modifyTodoCategory(@RequestBody + TodoCategoryRequestDTO category){ + return ResponseEntity.ok(todoCategoryService.updateCategory(category)); + } + + + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/TodoController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/TodoController.java index 01d1eb4..c2c2820 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/TodoController.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/TodoController.java @@ -1,9 +1,15 @@ package com.todoslave.feedme.controller; -import com.todoslave.feedme.domain.entity.task.Todo; +import com.todoslave.feedme.DTO.TodoCalendarResponseDTO; +import com.todoslave.feedme.DTO.TodoCreateRequestDTO; +import com.todoslave.feedme.DTO.TodoDailyRequestDTO; +import com.todoslave.feedme.DTO.TodoMainResponseDTO; +import com.todoslave.feedme.DTO.TodoModifyRequestDTO; +import com.todoslave.feedme.DTO.TodoRequestDTO; +import com.todoslave.feedme.DTO.TodoResponseDTO; import com.todoslave.feedme.service.TodoService; -import java.sql.Date; -import java.sql.Timestamp; + +import java.time.LocalDate; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; @@ -14,7 +20,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -23,35 +28,67 @@ @RequestMapping("/todos") public class TodoController { - @Autowired - private final TodoService todoservice; + private final TodoService todoService; + + // 할일 목록에서 일정(일) 불러오기 + @GetMapping("/todolist/daily") + public ResponseEntity> findDailyTodoList(@RequestBody + TodoDailyRequestDTO todoDailyRequestDTO){ + + return ResponseEntity.ok(todoService.getTodoListDaily(todoDailyRequestDTO)); + } + + // 메인 달력에서 일정 불러오기 + @GetMapping("/calendar/daily") + public ResponseEntity> findCalendarTodoList(@RequestBody TodoRequestDTO todoRequestDTO){ + + return ResponseEntity.ok(todoService.getTodoCalendarDaily(todoRequestDTO)); + } + + // 메인 화면에서 당일 안한 일정들 불러오기 + @GetMapping("/main/daily") + public ResponseEntity> findMainInCompleted(){ + + return ResponseEntity.ok(todoService.getTodoMainDaily()); + } + + // 월별 일정 완/미완 불러오기 + @GetMapping("/calendar") + public ResponseEntity> findCalendarIsCompleted(@RequestBody TodoRequestDTO todoRequestDTO){ - @GetMapping("/daily") - public ResponseEntity> findTodoDaily(@RequestHeader("Authorization") String token, @RequestBody Todo todo){ - Timestamp timestamp = todo.getCreatedAt(); - Date createdAt = new Date(timestamp.getTime()); - return ResponseEntity.ok(todoservice.getTodoDaily(todo.getMember().getId(), createdAt)); + return ResponseEntity.ok(todoService.getTodoCalendarCompleted(todoRequestDTO)); } + // 투두 생성 @PostMapping - public ResponseEntity createTodo(@RequestHeader("Authorization") String token, @RequestBody Todo todo){ - return ResponseEntity.ok(todoservice.insertTodo(todo)); + public ResponseEntity createTodo(@RequestBody TodoCreateRequestDTO todo){ + return ResponseEntity.ok(todoService.insertTodo(todo)); } + // 투두 삭제 @DeleteMapping("/{id}") - public ResponseEntity removeTodo(@RequestHeader("Authorization") String token, @PathVariable int id){ - todoservice.deleteTodo(id); + public ResponseEntity removeTodo(@PathVariable int id){ + todoService.deleteTodo(id); return ResponseEntity.noContent().build(); } + // 투두 수정 @PatchMapping() - public ResponseEntity modifyTodo(@RequestHeader("Authorization") String token, @RequestBody Todo todo){ - return ResponseEntity.ok(todoservice.updateTodoContent(todo.getId(), todo.getContent())); + public ResponseEntity modifyTodo(@RequestBody TodoModifyRequestDTO todoModifyRequestDTO){ + return ResponseEntity.ok(todoService.updateTodo(todoModifyRequestDTO)); } - @PostMapping("/{id}/complete") - public ResponseEntity completeTodo(@RequestHeader("Authorization") String token, @PathVariable int id){ - return ResponseEntity.ok(todoservice.completeTodo(id)); + // 투두 완료 + @PostMapping("/complete/{id}") + public ResponseEntity completeTodo(@PathVariable int id){ + return ResponseEntity.ok(todoService.completeTodo(id)); } + //오늘 (어제 포함) 일정 완료하기 + @PostMapping("/complete/complateAll") + public ResponseEntity allCompleteTodo(@RequestBody TodoRequestDTO todoRequestDTO){ + return ResponseEntity.ok(todoService.AllcompleteTodo(todoRequestDTO)); + } + + } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/UserViewController.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/UserViewController.java new file mode 100644 index 0000000..38e4d8e --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/controller/UserViewController.java @@ -0,0 +1,31 @@ +package com.todoslave.feedme.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +//웹 페이지 로그인 테스트용 +@Controller +public class UserViewController { + + @GetMapping("/login") + public String login() { + return "login"; + } + + @GetMapping("/signup") + public String signup() { + return "signup"; + } + + + + @GetMapping("/test") + public String test() { + return "test"; + } + + @GetMapping("/testsite") + public String testsite() { + return "testsite"; + } +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/Feed.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/Feed.java index 7d440e9..25160be 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/Feed.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/Feed.java @@ -7,6 +7,7 @@ import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -17,7 +18,7 @@ public class Feed { // 피드 아이디 @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; // 회원 ID @@ -26,6 +27,14 @@ public class Feed { @JsonBackReference private Member member; + //그림일기 날자 + @Column(name = "diary_day") + private LocalDate diaryDay; + + //닉네임 + @Column(name = "nickname") + private String nickname; + // 피드 내용 @Lob @Column(name = "content", columnDefinition = "TEXT") diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/FeedComment.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/FeedComment.java index 8bfaf62..9050f78 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/FeedComment.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/FeedComment.java @@ -16,7 +16,7 @@ public class FeedComment { //피드 댓글 ID @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; //피드 ID @@ -40,7 +40,6 @@ public class FeedComment { @Column(name = "created_at", nullable = false, updatable = false) private LocalDateTime createdAt; - // 대댓글과 매핑 @OneToMany(mappedBy = "feedComment", cascade = CascadeType.ALL) private List feedRecomments = new ArrayList<>(); diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/FeedRecomment.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/FeedRecomment.java index 1784ec9..4613334 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/FeedRecomment.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/Feed/FeedRecomment.java @@ -13,7 +13,7 @@ public class FeedRecomment { // 피드 대댓글 ID @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; //피드 ID diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/check/Alarm.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/alarm/Alarm.java similarity index 69% rename from Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/check/Alarm.java rename to Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/alarm/Alarm.java index f6fa86e..6984094 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/check/Alarm.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/alarm/Alarm.java @@ -14,7 +14,7 @@ public class Alarm { // 알람 ID @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; // 회원 ID @@ -23,7 +23,7 @@ public class Alarm { @JsonBackReference private Member member; - //컨텐츠 + // 내용 @Column(nullable = false) private String content; @@ -32,13 +32,14 @@ public class Alarm { @Column(name = "receive_at", nullable = false, updatable = false) private LocalDateTime receiveAt; -// @Column() -// private boolean read; + // 확인 했는 지 여부 + @Column(name = "is_checked") + private int isChecked; - //==연관관계 메서드==// - public void setMember(Member member) { - this.member = member; - member.getAlarms().add(this); - } +// //==연관관계 메서드==// +// public void setMember(Member member) { +// this.member = member; +// member.getAlarms().add(this); +// } } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/avatar/Creature.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/avatar/Creature.java index b4076fd..a285c95 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/avatar/Creature.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/avatar/Creature.java @@ -1,34 +1,47 @@ package com.todoslave.feedme.domain.entity.avatar; import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonManagedReference; import com.todoslave.feedme.domain.entity.membership.Member; import jakarta.persistence.*; import lombok.Data; +import lombok.ToString; @Entity @Data @Table(name = "creature") +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) +@ToString public class Creature { // 크리쳐 ID - @Id @GeneratedValue + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; //회원 ID - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", referencedColumnName = "id") @JsonBackReference private Member member; - //진화단계 + // 레벨 @Column(nullable = false) - private int level = 1 ; //레벨 1로 초기화 한다는 뜻 + private int level = 0 ; //레벨 1로 초기화 한다는 뜻 + + // 경험치 + @Column(name = "exp", nullable = false) + private int exp = 0 ; + + //크리쳐 이름 + @Column(name = "creature_name") + private String creatureName; //==연관관계 메서드==// public void setMember(Member member) { this.member = member; - member.getCreatures().add(this); + member.setCreature(this); } - } \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/Friend.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/Friend.java index dbb35d1..1cb6d8d 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/Friend.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/Friend.java @@ -11,7 +11,7 @@ public class Friend { //친구 ID @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; // 본인 회원번호 diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/FriendRequest.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/FriendRequest.java index c7a14a2..889a246 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/FriendRequest.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/FriendRequest.java @@ -11,7 +11,7 @@ public class FriendRequest { //친구요청 ID - @Id @GeneratedValue + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; //본인 회원번호 diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatMessage.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatMessage.java index 0ab1140..7488062 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatMessage.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatMessage.java @@ -9,6 +9,7 @@ import lombok.NoArgsConstructor; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; @Data @Builder @@ -19,11 +20,14 @@ public class MemberChatMessage { @Id private String id; + @Field("memberChatRoom_id") private String memberChatRoomId; - private String sendId; + @Field("send_id") + private int sendId; private String content; @CreatedDate + @Field("transmit_at") private LocalDateTime transmitAt; @Override diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatRoom.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatRoom.java index b3bd10c..9d65456 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatRoom.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatRoom.java @@ -13,6 +13,7 @@ import org.hibernate.annotations.CreationTimestamp; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; @Data @Builder @@ -23,9 +24,10 @@ public class MemberChatRoom { @Id private String id; - private List participantIds; - + @Field("participant_ids") + private List participantIds; @CreatedDate + @Field("created_at") private LocalDate createdAt; } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatRoomChecked.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatRoomChecked.java new file mode 100644 index 0000000..df8f0d6 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/communication/MemberChatRoomChecked.java @@ -0,0 +1,31 @@ +package com.todoslave.feedme.domain.entity.communication; + +import jakarta.persistence.Id; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Document(collection = "memberchatroomchecked") +public class MemberChatRoomChecked { + + @Id + private String id; + + @Field("memberChatRoom_id") + private String memberChatRoomId; + + @Field("member_id") + private int memberId; + + @Field("recent_check_time") + private LocalDateTime recentCheckTime; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/diary/PictureDiary.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/diary/PictureDiary.java index 3a11b46..5462671 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/diary/PictureDiary.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/diary/PictureDiary.java @@ -13,7 +13,7 @@ public class PictureDiary { // 그림일기 ID @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; // 회원 ID diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/membership/Member.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/membership/Member.java index 6c63644..9b0701f 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/membership/Member.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/membership/Member.java @@ -1,23 +1,29 @@ package com.todoslave.feedme.domain.entity.membership; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonManagedReference; + import com.todoslave.feedme.domain.entity.avatar.Creature; import com.todoslave.feedme.domain.entity.Feed.Feed; import com.todoslave.feedme.domain.entity.Feed.FeedComment; import com.todoslave.feedme.domain.entity.Feed.FeedLike; import com.todoslave.feedme.domain.entity.Feed.FeedRecomment; -import com.todoslave.feedme.domain.entity.check.Alarm; + import com.todoslave.feedme.domain.entity.communication.Friend; import com.todoslave.feedme.domain.entity.communication.FriendRequest; import com.todoslave.feedme.domain.entity.diary.PictureDiary; import com.todoslave.feedme.domain.entity.task.CreatureTodo; +import com.todoslave.feedme.domain.entity.task.DayOff; import com.todoslave.feedme.domain.entity.task.Todo; import com.todoslave.feedme.domain.entity.task.TodoCategory; import jakarta.persistence.*; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.CreationTimestamp; + import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.ArrayList; @@ -27,53 +33,45 @@ @Getter @Setter @Table(name = "member") -public class Member { +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) +public class Member { //유저 디테일은 사용자 인증 정보를 담아두는 인터페이스이다. //회원 ID @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; - //GeneratedValue에 관하여 설명 - /** - DB마다 다른데 어떤 DB는 seq를 만들기도 하고(MYSQL) 어떤 애들은 테이블을 만드는데, 이러면 항상 ID값이 보장이 된다. - persist 할때 필요 => 영속성 컨텍스트에 값을 딱 넣어야 하는데, 그때 이제 키/밸류가 되는데 - - **/ - + //GeneratedValue에 관하여 설명 /** + // DB마다 다른데 어떤 DB는 seq를 만들기도 하고(MYSQL) 어떤 애들은 테이블을 만드는데, 이러면 항상 ID값이 보장이 된다. + // persist 할때 필요 => 영속성 컨텍스트에 값을 딱 넣어야 하는데, 그때 이제 키/밸류가 되는데 + // + // **/ //이메일 @Column(nullable = false) private String email; //닉네임 - @Column(nullable = false) + @Column (nullable = false) private String nickname; //생일 + @Column //(nullable = false) private Timestamp birthday; - //토큰 - @Column(nullable = false) - private String token; - - //유저를 하나로 합침 - - // 경험치 - @Column(name = "exp", nullable = false, updatable = false) - private int exp; - // 상태 @Column(name = "status", nullable = false) @Enumerated(EnumType.STRING) - private Emotion status; // BASIC, JOY, SAD + private Emotion status = Emotion.BASIC; // BASIC, JOY, SAD //위도 - @Column(name = "latitude") + @Column private Double latitude; //경도 - @Column(name = "longitude") + @Column private Double longitude; //가입일 @@ -81,27 +79,17 @@ public class Member { @Column(name = "created_at", nullable = false, updatable = false) private LocalDateTime joinDate; - - // - - - - //여기부터 1대1 - -// //회원 상세와 매핑 -// @OneToOne(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) -// private MemberDetail memberDetail; -// -// //회원 갱신 정보와 매핑 -// @OneToOne(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) -// private MemberRenewInfo memberRenewInfo; -// -// //회원 위치와 매핑 -// @OneToOne(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) -// private MemberSpace memberSpace; + //유저 인증 정보 + @Column(name = "user_role") + private String userRole; //여기부터 1대 N + //크리쳐와 매핑 + @OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY, orphanRemoval = true) //1:1 중에 1을 맡는다. + @JsonManagedReference + private Creature creature; + //친구와 매핑 @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) @JsonManagedReference @@ -127,10 +115,10 @@ public class Member { @JsonManagedReference private List creatureTodos = new ArrayList<>(); - //크리쳐와 매핑 - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) - @JsonManagedReference - private List creatures = new ArrayList<>(); +// //크리쳐와 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) +// @JsonManagedReference +// private List creatures = new ArrayList<>(); // 그림일기와 매핑 @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) @@ -160,7 +148,222 @@ public class Member { //알람과 매핑 @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) @JsonManagedReference - private List alarms = new ArrayList<>(); + private List alarms = new ArrayList<>(); + //끝내는 날과 매핑 + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonManagedReference + private List dayOffs= new ArrayList<>(); } + +// 메인에 있던건데, 뭐에 쓰는교?! +// @Override +// public Collection getAuthorities() { +// return List.of(new SimpleGrantedAuthority("MEMBER")); +// } + +// @Override +// public String getPassword() { +// return ""; +// } + +// @Override +// public String getUsername() { +// return ""; +// } + + + +//package com.todoslave.feedme.domain.entity.membership; +// +//import com.fasterxml.jackson.annotation.JsonManagedReference; +//import com.todoslave.feedme.domain.entity.avatar.Creature; +//import com.todoslave.feedme.domain.entity.Feed.Feed; +//import com.todoslave.feedme.domain.entity.Feed.FeedComment; +//import com.todoslave.feedme.domain.entity.Feed.FeedLike; +//import com.todoslave.feedme.domain.entity.Feed.FeedRecomment; +//import com.todoslave.feedme.domain.entity.check.Alarm; +//import com.todoslave.feedme.domain.entity.communication.Friend; +//import com.todoslave.feedme.domain.entity.communication.FriendRequest; +//import com.todoslave.feedme.domain.entity.diary.PictureDiary; +//import com.todoslave.feedme.domain.entity.task.CreatureTodo; +//import com.todoslave.feedme.domain.entity.task.Todo; +//import com.todoslave.feedme.domain.entity.task.TodoCategory; +//import jakarta.persistence.*; +//import lombok.*; +//import org.hibernate.annotations.CreationTimestamp; +//import org.springframework.security.core.GrantedAuthority; +//import org.springframework.security.core.authority.SimpleGrantedAuthority; +//import org.springframework.security.core.userdetails.UserDetails; +// +//import java.io.Serializable; +//import java.sql.Timestamp; +//import java.time.LocalDateTime; +//import java.util.ArrayList; +//import java.util.Collection; +//import java.util.List; +// +//@Entity +//@Getter +//@Setter +//@Table(name = "member") +//@NoArgsConstructor +//@AllArgsConstructor +//public class Member implements UserDetails { //유저 디테일은 사용자 인증 정보를 담아두는 인터페이스이다. +// +// //회원 ID +// @Id +// @GeneratedValue(strategy = GenerationType.IDENTITY) +// private int id; +// +// //GeneratedValue에 관하여 설명 +// /** +// DB마다 다른데 어떤 DB는 seq를 만들기도 하고(MYSQL) 어떤 애들은 테이블을 만드는데, 이러면 항상 ID값이 보장이 된다. +// persist 할때 필요 => 영속성 컨텍스트에 값을 딱 넣어야 하는데, 그때 이제 키/밸류가 되는데 +// +// **/ +// +// //이메일 +// @Column(nullable = false) +// private String email; +// +// //닉네임 +// @Column //(nullable = false) +// private String nickname; +// +// //생일 +// @Column //(nullable = false) +// private Timestamp birthday; +// +// //토큰 +// @Column +// private String token; +// +// //유저를 하나로 합침 +// +// // 경험치 +// @Column(name = "exp", nullable = false, updatable = false) +// private int exp = 0 ; +// +// // 상태 +// @Column(name = "status", nullable = false) +// @Enumerated(EnumType.STRING) +// private Emotion status = Emotion.BASIC; // BASIC, JOY, SAD +// +// //위도 +// @Column +// private Double latitude; +// +// //경도 +// @Column +// private Double longitude; +// +// //가입일 +// @CreationTimestamp +// @Column(name = "created_at", nullable = false, updatable = false) +// private LocalDateTime joinDate; +// +// //유저 인증 정보 +// @Column(name = "user_role") +// private String userRole; +// +// //여기부터 1대 N +// +// //친구와 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) +// @JsonManagedReference +// private List friends = new ArrayList<>(); +// +// //친구요청과 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) +// @JsonManagedReference +// private List friendRequests = new ArrayList<>(); +// +// // 투두와 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) +// @JsonManagedReference +// private List todos = new ArrayList<>(); +// +// // 투두 카테고리와 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) +// @JsonManagedReference +// private List todoCategories = new ArrayList<>(); +// +// // 크리쳐 숙제와 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) +// @JsonManagedReference +// private List creatureTodos = new ArrayList<>(); +// +// //크리쳐와 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) +// @JsonManagedReference +// private List creatures = new ArrayList<>(); +// +// // 그림일기와 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) +// @JsonManagedReference +// private List pictureDiary = new ArrayList<>(); +// +// // 피드와 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) +// @JsonManagedReference +// private List feeds = new ArrayList<>(); +// +// //좋아요 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) +// @JsonManagedReference +// private List feedLikes = new ArrayList<>(); +// +// //피드 댓글과 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) +// @JsonManagedReference +// private List feedComments = new ArrayList<>(); +// +// //피드 대댓글과 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) +// @JsonManagedReference +// private List feedRecomments = new ArrayList<>(); +// +// //알람과 매핑 +// @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) +// @JsonManagedReference +// private List alarms = new ArrayList<>(); +// +// +// @Override +// public Collection getAuthorities() { +// return List.of(new SimpleGrantedAuthority("MEMBER")); +// } +// +// @Override +// public String getPassword() { +// return null; +// } +// +// @Override +// public String getUsername() { +// return email; +// } +// +// @Override +// public boolean isAccountNonExpired() { +// return true; +// } +// +// @Override +// public boolean isAccountNonLocked() { +// return true; +// } +// +// @Override +// public boolean isCredentialsNonExpired() { +// return true; +// } +// +// @Override +// public boolean isEnabled() { +// return true; +// } +// +//} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/membership/MemberAlarm.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/membership/MemberAlarm.java new file mode 100644 index 0000000..1adf1fb --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/membership/MemberAlarm.java @@ -0,0 +1,35 @@ +package com.todoslave.feedme.domain.entity.membership; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import java.sql.Time; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Getter +@Setter +@Table(name = "memberAlarm") +public class MemberAlarm { + + @Id + @GeneratedValue + private int id; + + // 회원 ID + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + @JsonBackReference + private Member member; + + @Column(name = "alarm_time") + private int alarmTime; + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/membership/RefreshToken.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/membership/RefreshToken.java new file mode 100644 index 0000000..eea440c --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/membership/RefreshToken.java @@ -0,0 +1,34 @@ +package com.todoslave.feedme.domain.entity.membership; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +@Entity +public class RefreshToken { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", updatable = false) + private int id; + + @Column(name = "user_id", nullable = false, unique = true) + private int userId; + + @Column(name = "refresh_token", nullable = false) + private String refreshToken; + + public RefreshToken(int userId, String refreshToken) { + this.userId = userId; + this.refreshToken = refreshToken; + } + + public RefreshToken update(String newRefreshToken) { + this.refreshToken = newRefreshToken; + + return this; + } +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/mission/Mission.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/mission/Mission.java new file mode 100644 index 0000000..2d6ba37 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/mission/Mission.java @@ -0,0 +1,28 @@ +package com.todoslave.feedme.domain.entity.mission; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Getter +@Setter +@NoArgsConstructor +public class Mission { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String weatherCondition; + private String mission; + + public Mission(String weatherCondition, String mission) { + this.weatherCondition = weatherCondition; + this.mission = mission; + } +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/CreatureTodo.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/CreatureTodo.java index 581d687..f9b7d21 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/CreatureTodo.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/CreatureTodo.java @@ -7,6 +7,7 @@ import org.hibernate.annotations.CreationTimestamp; import java.sql.Timestamp; +import java.time.LocalDate; @Entity @@ -16,7 +17,7 @@ public class CreatureTodo { //크리쳐 숙제 ID @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; // 회원 ID @@ -32,11 +33,11 @@ public class CreatureTodo { // 생성일자 @CreationTimestamp @Column(name = "created_at", nullable = false, updatable = false) - private Timestamp createdAt; + private LocalDate createdAt; // 완료여부 @Column(name = "is_completed") - private boolean isCompleted; + private int isCompleted = 0; //==연관관계 메서드==// public void setMember(Member member) { diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/DayOff.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/DayOff.java new file mode 100644 index 0000000..643a302 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/DayOff.java @@ -0,0 +1,35 @@ +package com.todoslave.feedme.domain.entity.task; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.todoslave.feedme.domain.entity.membership.Member; +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDate; + +@Entity +@Data +@Table(name = "dayoff") +public class DayOff { + + //날자 ID + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + // 회원 ID + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + @JsonBackReference + private Member member; + + // 생성일자 + @Column(name = "end_day", nullable = false) + private LocalDate endDay; + + // 연관관계 메서드 + public void setMember(Member member) { + this.member = member; + member.getDayOffs().add(this); + } +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/Todo.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/Todo.java index 412a98e..6f8ffc9 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/Todo.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/Todo.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonBackReference; import com.todoslave.feedme.domain.entity.membership.Member; import jakarta.persistence.*; +import java.time.LocalDate; import lombok.Data; import lombok.Getter; import org.hibernate.annotations.CreationTimestamp; @@ -15,7 +16,7 @@ public class Todo { // 할일 ID - @Id @GeneratedValue + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; // 회원 ID @@ -36,11 +37,11 @@ public class Todo { // 생성 일자 @CreationTimestamp @Column(name = "created_at", nullable = false, updatable = false) - private Timestamp createdAt; + private LocalDate createdAt; //완료 여부 @Column(name = "is_completed",nullable = false) - private boolean isCompleted; + private int isCompleted; //==연관관계 메서드==// public void setMember(Member member) { diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/TodoCategory.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/TodoCategory.java index 3e9fa54..71592b1 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/TodoCategory.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/domain/entity/task/TodoCategory.java @@ -14,7 +14,7 @@ public class TodoCategory { // 할일 카테고리 ID - @Id @GeneratedValue + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; // 회원 ID diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/event/AlarmCreatedEvent.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/event/AlarmCreatedEvent.java new file mode 100644 index 0000000..0bf84af --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/event/AlarmCreatedEvent.java @@ -0,0 +1,25 @@ +package com.todoslave.feedme.event; + +import com.todoslave.feedme.domain.entity.check.Alarm; +import org.springframework.context.ApplicationEvent; + +public class AlarmCreatedEvent extends ApplicationEvent { + + private final Alarm alarm; + private String alarmType; + + public AlarmCreatedEvent(Object source, Alarm alarm, String type) { + super(source); + this.alarm = alarm; + this.alarmType = type; + } + + public Alarm getAlarm(){ + return alarm; + } + + public String getAlarmType(){ + return alarmType; + } + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/event/AlarmEventListener.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/event/AlarmEventListener.java new file mode 100644 index 0000000..edcfe93 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/event/AlarmEventListener.java @@ -0,0 +1,90 @@ +package com.todoslave.feedme.event; + +import com.todoslave.feedme.domain.entity.check.Alarm; +import com.todoslave.feedme.service.AlarmService; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import lombok.RequiredArgsConstructor; +import org.springframework.context.event.EventListener; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +@RequiredArgsConstructor +public class AlarmEventListener { + + private final Map emitters = new ConcurrentHashMap<>(); + + private AlarmService alarmService; + + // 투두 알림 + @EventListener(condition = "#event.alarmType == 'Todo'") + public void handlerAlarmTodoEvent(AlarmCreatedEvent event){ + + Alarm alarm = event.getAlarm(); + + SseEmitter emitter = emitters.get(alarm.getMember().getId()); + + if(emitter != null){ + + try{ + emitter.send(SseEmitter.event().name("Todo").data(alarm.getContent())); + } catch (IOException e){ + emitters.remove(alarm.getMember().getId()); + } + + } + + } + + // 친구 요청 + @EventListener(condition = "#event.alarmType == 'Friend'") + public void handlerAlarmFriendEvent(AlarmCreatedEvent event){ + + Alarm alarm = event.getAlarm(); + + SseEmitter emitter = emitters.get(alarm.getMember()); + + if(emitter != null){ + + try{ + emitter.send(SseEmitter.event().name("Friend").data(alarm.getContent())); + } catch (IOException e){ + emitters.remove(alarm.getMember().getId()); + } + + } + + } + + // 생일 축하 + @EventListener(condition = "#event.alarmType == 'Birthday'") + public void handlerAlarmBirthdayEvent(AlarmCreatedEvent event){ + + Alarm alarm = event.getAlarm(); + + SseEmitter emitter = emitters.get(alarm.getMember().getId()); + + if(emitter != null){ + + try{ + emitter.send(SseEmitter.event().name("Birthday").data(alarm.getContent())); + } catch (IOException e){ + emitters.remove(alarm.getMember().getId()); + } + + } + + } + + // 채팅 갱신 알림 + + + public void addEmitter(int memberId, SseEmitter emitter){ + emitters.put(memberId, emitter); + } + + public void removeEmitter(int memberId){ + emitters.remove(memberId); + } + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Handler/JWTUtill.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Handler/JWTUtill.java new file mode 100644 index 0000000..6a9c75e --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Handler/JWTUtill.java @@ -0,0 +1,146 @@ +package com.todoslave.feedme.login.Handler; + +import com.todoslave.feedme.config.jwt.JwtProperties; +import com.todoslave.feedme.login.Service.RefreshTokenService; +import com.todoslave.feedme.login.Service.TokenBlacklistService; +import com.todoslave.feedme.login.dto.GeneratedToken; +import com.todoslave.feedme.service.MemberService; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Base64; +import java.util.Date; + +//토큰 발행 및 토큰 검증에 쓰이는 유틸 입니다. + +@Slf4j +@Service +@RequiredArgsConstructor +public class JWTUtill { + private final JwtProperties jwtProperties; + private final RefreshTokenService tokenService; + private final MemberService memberService; + private final TokenBlacklistService tokenBlacklistService; + private String secretKey; + + @PostConstruct + protected void init() { + secretKey = Base64.getEncoder().encodeToString(jwtProperties.getSecretKey().getBytes()); + } + + public GeneratedToken generateToken(String email, String role) { + // refreshToken과 accessToken을 생성한다. + String refreshToken = generateRefreshToken(email, role); + String accessToken = generateAccessToken(email, role); + + // 토큰을 Redis에 저장한다. + tokenService.saveTokenInfo(email, refreshToken, accessToken); + + //토큰을 MY SQL에 저장한다. +// memberService.settoken(email, refreshToken); + + return new GeneratedToken(accessToken, refreshToken); + } + + + + public String generateRefreshToken(String email, String role) { + // 토큰의 유효 기간을 밀리초 단위로 설정. + long refreshPeriod = 1000L * 60L * 60L * 24L * 14; // 2주 + + // 새로운 클레임 객체를 생성하고, 이메일과 역할(권한)을 셋팅 + Claims claims = Jwts.claims().setSubject(email); + claims.put("role", role); + + // 현재 시간과 날짜를 가져온다. + Date now = new Date(); + + return Jwts.builder() + // Payload를 구성하는 속성들을 정의한다. + .setClaims(claims) + // 발행일자를 넣는다. + .setIssuedAt(now) + // 토큰의 만료일시를 설정한다. + .setExpiration(new Date(now.getTime() + refreshPeriod)) + // 지정된 서명 알고리즘과 비밀 키를 사용하여 토큰을 서명한다. + .signWith(SignatureAlgorithm.HS256, secretKey) + .compact(); + } + + + public String generateAccessToken(String email, String role) { + long tokenPeriod = 1000L * 60L * 30L * 4; // 30분 +// long tokenPeriod = 1000L * 30L; + Claims claims = Jwts.claims().setSubject(email); + claims.put("role", role); + + Date now = new Date(); + return + Jwts.builder() + // Payload를 구성하는 속성들을 정의한다. + .setClaims(claims) + // 발행일자를 넣는다. + .setIssuedAt(now) + // 토큰의 만료일시를 설정한다. + .setExpiration(new Date(now.getTime() + tokenPeriod)) + // 지정된 서명 알고리즘과 비밀 키를 사용하여 토큰을 서명한다. + .signWith(SignatureAlgorithm.HS256, secretKey) + .compact(); + + } + + +// public boolean verifyToken(String token) { //토큰 검증 하기 (시간 체크) +// try { +// Jws claims = Jwts.parser() +// .setSigningKey(secretKey) // 비밀키를 설정하여 파싱한다. +// .parseClaimsJws(token); // 주어진 토큰을 파싱하여 Claims 객체를 얻는다. +// // 토큰의 만료 시간과 현재 시간비교 +// return claims.getBody() +// .getExpiration() +// .after(new Date()); // 만료 시간이 현재 시간 이후인지 확인하여 유효성 검사 결과를 반환 +// } catch (Exception e) { +// return false; +// } +// } +public boolean verifyToken(String token) { + try { + if (tokenBlacklistService.isBlacklisted(token)) { + return false; + } + Jws claims = Jwts.parser() + .setSigningKey(secretKey) + .parseClaimsJws(token); + return claims.getBody() + .getExpiration() + .after(new Date()); + } catch (Exception e) { + return false; + } +} + //토큰을 블랙리스트에 추가 + public void invalidateToken(String token) { + // 토큰의 만료 날짜를 얻음 + Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody(); + Date expiryDate = claims.getExpiration(); + // 블랙리스트 서비스에 토큰을 추가 + tokenBlacklistService.addToBlacklist(token, expiryDate); + } + + // 토큰에서 Email을 추출한다. + public String getUid(String token) { + return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject(); + } + + // 토큰에서 ROLE(권한)만 추출한다. + public String getRole(String token) { + return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().get("role", String.class); + } + +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Handler/MyAuthenticationFailureHandler.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Handler/MyAuthenticationFailureHandler.java new file mode 100644 index 0000000..bb06871 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Handler/MyAuthenticationFailureHandler.java @@ -0,0 +1,23 @@ +package com.todoslave.feedme.login.Handler; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +//인증 실패시 이동 +@Component +@Slf4j +public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + // 인증 실패시 메인 페이지로 이동 + response.sendRedirect("http://localhost:3000/"); + } + +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Handler/MyAuthenticationSuccessHandler.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Handler/MyAuthenticationSuccessHandler.java new file mode 100644 index 0000000..e5e8d65 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Handler/MyAuthenticationSuccessHandler.java @@ -0,0 +1,85 @@ +package com.todoslave.feedme.login.Handler; + +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.login.Handler.JWTUtill; +import com.todoslave.feedme.login.dto.GeneratedToken; +import com.todoslave.feedme.repository.MemberRepository; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +//로그인 성공시 이동 + +@Slf4j +@Component +@RequiredArgsConstructor +public class MyAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { + + private final JWTUtill jwtUtil; + private final MemberRepository memberRepository; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + + // OAuth2User로 캐스팅하여 인증된 사용자 정보를 가져온다. + OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); + // 사용자 이메일을 가져온다. + String email = oAuth2User.getAttribute("email"); + // 서비스 제공 플랫폼(GOOGLE, KAKAO, NAVER)이 어디인지 가져온다. + String provider = oAuth2User.getAttribute("provider"); + + // CustomOAuth2UserService에서 셋팅한 로그인한 회원 존재 여부를 가져온다. + boolean isExist = oAuth2User.getAttribute("exist"); + // OAuth2User로 부터 Role을 얻어온다. + String role = oAuth2User.getAuthorities().stream(). + findFirst() // 첫번째 Role을 찾아온다. + .orElseThrow(IllegalAccessError::new) // 존재하지 않을 시 예외를 던진다. + .getAuthority(); // Role을 가져온다. + + if (isExist) { + // 회원이 존재하면 jwt token 발행을 시작한다. + GeneratedToken token = jwtUtil.generateToken(email, role); + log.info("jwtToken = {}", token.getAccessToken()); + + // 해당 멤버의 크리쳐가 있는지 체크하기 + Member member = memberRepository.findByEmail(email).orElse(null); + boolean hasCreature = member != null && member.getCreature() != null; + +// // accessToken을 헤더에 추가 +// response.setHeader("Authorization", "Bearer " + token.getAccessToken()); + + // 크리쳐 존재 여부를 쿼리스트링에 담는 url을 만들어준다. + String targetUrl = UriComponentsBuilder.fromUriString("http://localhost:3000/LoginLoding") + .queryParam("hasCreature", hasCreature) + .queryParam("accessToken", token.getAccessToken()) + .build() + .encode(StandardCharsets.UTF_8) + .toUriString(); + log.info("redirect 준비"); + // 로그인 확인 페이지로 리다이렉트 시킨다. + getRedirectStrategy().sendRedirect(request, response, targetUrl); + + } else { + + // 회원이 존재하지 않을경우, 서비스 제공자와 email을 쿼리스트링으로 전달하는 url을 만들어준다. + String targetUrl = UriComponentsBuilder.fromUriString("http://localhost:3000/Signup") + .queryParam("email", (String) oAuth2User.getAttribute("email")) + .queryParam("provider", provider) + .build() + .encode(StandardCharsets.UTF_8) + .toUriString(); + // 회원가입 페이지로 리다이렉트 시킨다. + getRedirectStrategy().sendRedirect(request, response, targetUrl); + } + } +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Service/CustomOAuth2UserService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Service/CustomOAuth2UserService.java new file mode 100644 index 0000000..b4d0290 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Service/CustomOAuth2UserService.java @@ -0,0 +1,91 @@ +package com.todoslave.feedme.login.Service; + +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.login.dto.OAuth2Attribute; +import com.todoslave.feedme.service.MemberService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +@Slf4j +@Service +@RequiredArgsConstructor +public class CustomOAuth2UserService implements OAuth2UserService { + + private final MemberService memberService; + + @Override // 유저를 가져오려고 한다.OAuth2User 라는 객체 형태로 + public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { + + // 기본 OAuth2UserService 객체 생성 과정 + OAuth2UserService oAuth2UserService = new DefaultOAuth2UserService(); + + // OAuth2UserService를 사용하여 OAuth2User 정보를 가져온다. + OAuth2User oAuth2User = oAuth2UserService.loadUser(userRequest); + + // 클라이언트 등록 ID(naver, kakao)와 사용자 이름 속성을 가져온다. + String registrationId = userRequest.getClientRegistration().getRegistrationId(); + + String userNameAttributeName = userRequest.getClientRegistration() //사용자 이름 가져오기 + .getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName(); + +// System.out.println(registrationId); +// System.out.println(userNameAttributeName); +// System.out.println("이거야!!"); + + // OAuth2UserService를 사용하여 가져온 OAuth2User 정보로 OAuth2Attribute 객체를 만든다. + OAuth2Attribute oAuth2Attribute = + OAuth2Attribute.of(registrationId, userNameAttributeName, oAuth2User.getAttributes()); + + // OAuth2Attribute의 속성값들을 Map으로 반환 받는다. + Map memberAttribute = oAuth2Attribute.convertToMap(); + + // 사용자 email(또는 id) 정보를 가져온다. + String email = (String) memberAttribute.get("email"); + + System.out.println(email); + + + // 이메일로 가입된 회원인지 조회한다. + Optional findMember = memberService.findByEmail(email); + + if (findMember.isEmpty()) { + // 회원이 존재하지 않을경우, memberAttribute의 exist 값을 false로 넣어준다. + + memberAttribute.put("exist", false); + // 회원의 권한(회원이 존재하지 않으므로 기본권한인 ROLE_USER를 넣어준다), + // 회원속성, 속성이름을 이용해 DefaultOAuth2User 객체를 생성해 반환한다. + return new DefaultOAuth2User( + Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")), + memberAttribute, "email"); + } + + //일단 아이디 잘 따지나 체크 + System.out.println(findMember.get().getId()); + System.out.println("ROLE_".concat(findMember.get().getUserRole())); + + // 여기는 회원인 사람 -> SuccessHandler 에서 jwt 토큰을 발급해서 프론트로 넘겨 주게 된다!! + + // 회원이 존재할경우, memberAttribute의 exist 값을 true로 넣어준다. + //SuccessHandler 에서 exist 변수의 값에 따라서 회원가입을 했는지 안했는지 여부를 체크하고 처리할 수 있게 넣어준다. + memberAttribute.put("exist", true); + + + // 회원의 권한과, 회원속성, 속성이름을 이용해 DefaultOAuth2User 객체를 생성해 반환한다. + return new DefaultOAuth2User( + Collections.singleton(new SimpleGrantedAuthority("ROLE_".concat(findMember.get().getUserRole()))), + memberAttribute, "email"); + + } +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Service/RefreshTokenService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Service/RefreshTokenService.java new file mode 100644 index 0000000..bd04f2c --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Service/RefreshTokenService.java @@ -0,0 +1,29 @@ +package com.todoslave.feedme.login.Service; + +import com.todoslave.feedme.login.dto.RefreshToken; +import com.todoslave.feedme.repository.RefreshTokenRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class RefreshTokenService { + + private final RefreshTokenRepository repository; + + @Transactional + public void saveTokenInfo(String email, String refreshToken, String accessToken) { + repository.save(new RefreshToken(email, accessToken, refreshToken)); + } + + @Transactional + public void removeRefreshToken(String accessToken) { + RefreshToken token = repository.findByAccessToken(accessToken) + .orElseThrow(IllegalArgumentException::new); + + repository.delete(token); + } + + +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Service/TokenBlacklistService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Service/TokenBlacklistService.java new file mode 100644 index 0000000..dd807e5 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/Service/TokenBlacklistService.java @@ -0,0 +1,27 @@ +package com.todoslave.feedme.login.Service; + +import com.todoslave.feedme.login.TokenBlacklist; +import com.todoslave.feedme.repository.TokenBlacklistRepository; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class TokenBlacklistService { + + private final TokenBlacklistRepository tokenBlacklistRepository; + + public void addToBlacklist(String token, Date expiryDate) { + TokenBlacklist tokenBlacklist = new TokenBlacklist(token, expiryDate); + tokenBlacklistRepository.save(tokenBlacklist); + } + + public boolean isBlacklisted(String token) { + Optional tokenBlacklist = tokenBlacklistRepository.findByToken(token); + return tokenBlacklist.isPresent(); + } +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/TokenBlacklist.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/TokenBlacklist.java new file mode 100644 index 0000000..5da0111 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/TokenBlacklist.java @@ -0,0 +1,36 @@ +package com.todoslave.feedme.login; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Column; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Date; + +@Entity +@Table(name = "token_blacklist") +@Getter +@Setter +@NoArgsConstructor +public class TokenBlacklist { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String token; + + @Column(nullable = false) + private Date expiryDate; + + public TokenBlacklist(String token, Date expiryDate) { + this.token = token; + this.expiryDate = expiryDate; + } +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/GeneratedToken.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/GeneratedToken.java new file mode 100644 index 0000000..2c73881 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/GeneratedToken.java @@ -0,0 +1,15 @@ +package com.todoslave.feedme.login.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +@Getter +@AllArgsConstructor +@Builder @ToString +public class GeneratedToken { + + private String accessToken; + private String refreshToken; +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/OAuth2Attribute.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/OAuth2Attribute.java new file mode 100644 index 0000000..5dcf530 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/OAuth2Attribute.java @@ -0,0 +1,189 @@ +package com.todoslave.feedme.login.dto;//package com.todoslave.feedme.login.set; +// +//import lombok.AccessLevel; +//import lombok.Builder; +//import lombok.Getter; +//import lombok.ToString; +// +//import java.util.HashMap; +//import java.util.Map; +// +//@ToString +//@Builder(access = AccessLevel.PRIVATE) // Builder 메서드를 외부에서 사용하지 않으므로, Private 제어자로 지정 +//@Getter +//public class OAuth2Attribute { +// private Map attributes; // 사용자 속성 정보를 담는 Map +// private String attributeKey; // 사용자 속성의 키 값 +// private String email; // 이메일 정보 +//// private String name; // 이름 정보 +//// private String picture; // 프로필 사진 정보 +// private String provider; // 제공자 정보 +// +// // 서비스에 따라 OAuth2Attribute 객체를 생성하는 메서드 +// public static OAuth2Attribute of(String provider, String attributeKey, +// Map attributes) { +// switch (provider) { +// case "google": +// return ofGoogle(provider, attributeKey, attributes); +// case "kakao": +// return ofKakao(provider,"email", attributes); +// case "naver": +// return ofNaver(provider, "id", attributes); +// default: +// throw new RuntimeException(); +// } +// } +// +// /* +// * Google 로그인일 경우 사용하는 메서드, 사용자 정보가 따로 Wrapping 되지 않고 제공되어, +// * 바로 get() 메서드로 접근이 가능하다. +// * */ +// private static OAuth2Attribute ofGoogle(String provider, String attributeKey, +// Map attributes) { +// return OAuth2Attribute.builder() +// .email((String) attributes.get("email")) +// .provider(provider) +// .attributes(attributes) +// .attributeKey(attributeKey) +// .build(); +// } +// +// /* +// * Kakao 로그인일 경우 사용하는 메서드, 필요한 사용자 정보가 kakaoAccount -> kakaoProfile 두번 감싸져 있어서, +// * 두번 get() 메서드를 이용해 사용자 정보를 담고있는 Map을 꺼내야한다. +// * */ +// private static OAuth2Attribute ofKakao(String provider, String attributeKey, +// Map attributes) { +// Map kakaoAccount = (Map) attributes.get("kakao_account"); +// Map kakaoProfile = (Map) kakaoAccount.get("profile"); +// +// return OAuth2Attribute.builder() +// .email((String) kakaoAccount.get("email")) +// .provider(provider) +// .attributes(kakaoAccount) +// .attributeKey(attributeKey) +// .build(); +// } +// +// /* +// * Naver 로그인일 경우 사용하는 메서드, 필요한 사용자 정보가 response Map에 감싸져 있어서, +// * 한번 get() 메서드를 이용해 사용자 정보를 담고있는 Map을 꺼내야한다. +// * */ +// private static OAuth2Attribute ofNaver(String provider, String attributeKey, +// Map attributes) { +// Map response = (Map) attributes.get("response"); +// +// return OAuth2Attribute.builder() +// .email((String) response.get("email")) +// .attributes(response) +// .provider(provider) +// .attributeKey(attributeKey) +// .build(); +// } +// +// // OAuth2User 객체에 넣어주기 위해서 Map으로 값들을 반환해준다. +// public Map convertToMap() { +// Map map = new HashMap<>(); +// map.put("id", attributeKey); +// map.put("key", attributeKey); +// map.put("email", email); +// map.put("provider", provider); +// +// return map; +// } +//} + + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +import java.util.HashMap; +import java.util.Map; + +@ToString +@Builder(access = AccessLevel.PRIVATE) // Builder 메서드를 외부에서 사용하지 않으므로, Private 제어자로 지정 +@Getter +public class OAuth2Attribute { + private Map attributes; // 사용자 속성 정보를 담는 Map + private String attributeKey; // 사용자 속성의 키 값 + private String email; // 이메일 정보 +// private String name; // 이름 정보 +// private String picture; // 프로필 사진 정보 + private String provider; // 제공자 정보 + + // 서비스에 따라 OAuth2Attribute 객체를 생성하는 메서드 + public static OAuth2Attribute of(String provider, String attributeKey, + Map attributes) { + switch (provider) { + case "google": + return ofGoogle(provider, attributeKey, attributes); + case "kakao": + return ofKakao(provider,"email", attributes); + case "naver": + return ofNaver(provider, "id", attributes); + default: + throw new RuntimeException(); + } + } + + /* + * Google 로그인일 경우 사용하는 메서드, 사용자 정보가 따로 Wrapping 되지 않고 제공되어, + * 바로 get() 메서드로 접근이 가능하다. + * */ + private static OAuth2Attribute ofGoogle(String provider, String attributeKey, + Map attributes) { + return OAuth2Attribute.builder() + .email((String) attributes.get("email")) + .provider(provider) + .attributes(attributes) + .attributeKey(attributeKey) + .build(); + } + + /* + * Kakao 로그인일 경우 사용하는 메서드, 필요한 사용자 정보가 kakaoAccount -> kakaoProfile 두번 감싸져 있어서, + * 두번 get() 메서드를 이용해 사용자 정보를 담고있는 Map을 꺼내야한다. + * */ + private static OAuth2Attribute ofKakao(String provider, String attributeKey, + Map attributes) { + Map kakaoAccount = (Map) attributes.get("kakao_account"); + Map kakaoProfile = (Map) kakaoAccount.get("profile"); + + return OAuth2Attribute.builder() + .email((String) kakaoAccount.get("email")) + .provider(provider) + .attributes(kakaoAccount) + .attributeKey(attributeKey) + .build(); + } + + /* + * Naver 로그인일 경우 사용하는 메서드, 필요한 사용자 정보가 response Map에 감싸져 있어서, + * 한번 get() 메서드를 이용해 사용자 정보를 담고있는 Map을 꺼내야한다. + * */ + private static OAuth2Attribute ofNaver(String provider, String attributeKey, + Map attributes) { + Map response = (Map) attributes.get("response"); + + return OAuth2Attribute.builder() + .email((String) response.get("email")) + .attributes(response) + .provider(provider) + .attributeKey(attributeKey) + .build(); + } + + + // OAuth2User 객체에 넣어주기 위해서 Map으로 값들을 반환해준다. + public Map convertToMap() { + Map map = new HashMap<>(); + map.put("id", attributeKey); + map.put("key", attributeKey); + map.put("email", email); + map.put("provider", provider); + + return map; + } +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/RefreshToken.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/RefreshToken.java new file mode 100644 index 0000000..0fce16a --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/RefreshToken.java @@ -0,0 +1,28 @@ +package com.todoslave.feedme.login.dto; + +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.index.Indexed; + +import java.io.Serializable; + +@Getter +@AllArgsConstructor +@RedisHash(value = "jwtToken", timeToLive = 60 * 60 * 24 * 14) +public class RefreshToken implements Serializable { + + @Id + private String id; + + @Indexed + private String accessToken; + + private String refreshToken; + + public void updateAccessToken(String accessToken) { + this.accessToken = accessToken; + } + +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/TokenResponseStatus.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/TokenResponseStatus.java new file mode 100644 index 0000000..fe1ee28 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/dto/TokenResponseStatus.java @@ -0,0 +1,16 @@ +package com.todoslave.feedme.login.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class TokenResponseStatus { + + private Integer status; + private String accessToken; + + public static TokenResponseStatus addStatus(Integer status, String accessToken) { + return new TokenResponseStatus(status, accessToken); + } +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/util/CookieUtil.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/util/CookieUtil.java new file mode 100644 index 0000000..4e0c55c --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/util/CookieUtil.java @@ -0,0 +1,50 @@ +package com.todoslave.feedme.login.util; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.util.SerializationUtils; + +import java.util.Base64; + +public class CookieUtil { + + public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) { + Cookie cookie = new Cookie(name, value); + cookie.setPath("/"); + cookie.setMaxAge(maxAge); + + response.addCookie(cookie); + } + + public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String name) { + Cookie[] cookies = request.getCookies(); + + if (cookies == null) { + return; + } + + for (Cookie cookie : cookies) { + if (name.equals(cookie.getName())) { + cookie.setValue(""); + cookie.setPath("/"); + cookie.setMaxAge(0); + response.addCookie(cookie); + } + } + } + + public static String serialize(Object obj) { + return Base64.getUrlEncoder() + .encodeToString(SerializationUtils.serialize(obj)); + } + + public static T deserialize(Cookie cookie, Class cls) { + return cls.cast( + SerializationUtils.deserialize( + Base64.getUrlDecoder().decode(cookie.getValue()) + ) + ); + } +} + diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/util/SecurityUserDto.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/util/SecurityUserDto.java new file mode 100644 index 0000000..1fd83a5 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/util/SecurityUserDto.java @@ -0,0 +1,16 @@ +package com.todoslave.feedme.login.util; + + +import lombok.*; + +@NoArgsConstructor +@Getter +@ToString +@AllArgsConstructor +@Builder +public class SecurityUserDto { + private Integer id; + private String email; + private String nickname; + private String role; +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/util/SecurityUtil.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/util/SecurityUtil.java new file mode 100644 index 0000000..4b17487 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/login/util/SecurityUtil.java @@ -0,0 +1,42 @@ +package com.todoslave.feedme.login.util; + +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.repository.MemberRepository; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.Authentication; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SecurityUtil { + + private static MemberRepository memberRepository; + + @Autowired + public SecurityUtil(MemberRepository memberRepository) { + SecurityUtil.memberRepository = memberRepository; + } + + public static SecurityUserDto getCurrentUser() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication != null && authentication.getPrincipal() instanceof SecurityUserDto) { + return (SecurityUserDto) authentication.getPrincipal(); + } + + return null; // 인증 정보가 없는 경우 처리 + } + + public static int getCurrentUserId() { + SecurityUserDto userDto = getCurrentUser(); + return (userDto != null) ? userDto.getId() : -1; // -1로 대체하여 null 피하기 + } + + public static Member getCurrentMember() { + SecurityUserDto userDto = getCurrentUser(); + if (userDto != null) { + return memberRepository.findById(userDto.getId()).orElse(null); + } + return null; + } +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/AlarmRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/AlarmRepository.java index 01317fa..1ef8eab 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/AlarmRepository.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/AlarmRepository.java @@ -6,7 +6,7 @@ public interface AlarmRepository extends JpaRepository { -// List findByMemberIdAndReadFalse(int memberId, boolean read); + List findByMemberIdAndIsChecked(int memberId, int isChecked); } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/CreatureRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/CreatureRepository.java new file mode 100644 index 0000000..de1890d --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/CreatureRepository.java @@ -0,0 +1,10 @@ +package com.todoslave.feedme.repository; + +import com.todoslave.feedme.domain.entity.avatar.Creature; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CreatureRepository extends JpaRepository { + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/CreatureTodoReposito.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/CreatureTodoReposito.java new file mode 100644 index 0000000..502b637 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/CreatureTodoReposito.java @@ -0,0 +1,20 @@ +package com.todoslave.feedme.repository; + +import com.todoslave.feedme.domain.entity.task.CreatureTodo; +import com.todoslave.feedme.domain.entity.task.Todo; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.sql.Timestamp; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +@Repository +public interface CreatureTodoReposito extends JpaRepository { + List findByMemberIdAndCreatedAt(int memberId, LocalDate createdAt); + Optional findById(int memberId); + + // 특정 날짜와 완료 여부로 CreatureTodo의 개수를 계산하는 메서드 + long countByCreatedAtAndIsCompleted(LocalDate createdAt, int isCompleted); +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/DayOffRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/DayOffRepository.java new file mode 100644 index 0000000..7b6ccb6 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/DayOffRepository.java @@ -0,0 +1,17 @@ +package com.todoslave.feedme.repository; + +import com.todoslave.feedme.domain.entity.task.DayOff; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +@Repository +public interface DayOffRepository extends JpaRepository { + + // 특정 회원의 특정 날짜의 DayOff를 찾습니다. + Optional findByMemberIdAndEndDay(int memberId, LocalDate endDay); + long countByMemberIdAndEndDay(int memberId, LocalDate date); +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FeedCommentRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FeedCommentRepository.java new file mode 100644 index 0000000..60cf365 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FeedCommentRepository.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.repository; + +import com.todoslave.feedme.domain.entity.Feed.Feed; +import com.todoslave.feedme.domain.entity.Feed.FeedComment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface FeedCommentRepository extends JpaRepository { + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FeedRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FeedRepository.java new file mode 100644 index 0000000..f151401 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FeedRepository.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.repository; + +import com.todoslave.feedme.domain.entity.Feed.Feed; +import com.todoslave.feedme.domain.entity.task.DayOff; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface FeedRepository extends JpaRepository { + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FriendRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FriendRepository.java index ef56edc..ae50dbe 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FriendRepository.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FriendRepository.java @@ -2,13 +2,17 @@ import com.todoslave.feedme.domain.entity.communication.Friend; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import java.util.List; +@Repository public interface FriendRepository extends JpaRepository { List findAllByMemberId(int memberId); + boolean existsByMemberIdAndCounterpartId(int memberId, int counterpartId); + boolean existsByCounterpartIdAndMemberId(int counterpartId, int memberId); } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FriendRequestRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FriendRequestRepository.java index 6c3d42e..36dc2b9 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FriendRequestRepository.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/FriendRequestRepository.java @@ -1,7 +1,9 @@ package com.todoslave.feedme.repository; +import com.todoslave.feedme.domain.entity.communication.Friend; import com.todoslave.feedme.domain.entity.communication.FriendRequest; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import java.util.List; @@ -10,4 +12,6 @@ public interface FriendRequestRepository extends JpaRepository findAllByMemberId(int memberId); + + } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberAlarmRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberAlarmRepository.java new file mode 100644 index 0000000..1ca777a --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberAlarmRepository.java @@ -0,0 +1,14 @@ +package com.todoslave.feedme.repository; + +import com.todoslave.feedme.domain.entity.membership.MemberAlarm; +import java.sql.Time; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + + +public interface MemberAlarmRepository extends JpaRepository { + + boolean existsByMemberIdAndAlarmTime(@Param("memberId") int memberId, @Param("alarmTime") int alarmTime); + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberChatRoomRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberChatRoomRepository.java index 549646a..d71bd17 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberChatRoomRepository.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberChatRoomRepository.java @@ -11,10 +11,10 @@ public interface MemberChatRoomRepository extends MongoRepository participantIds); + MemberChatRoom findByParticipantIdsContainingAll(List participantIds); // 방 여러개 얻어오기 - List findAllByParticipantIdsContaining(List participantIds); + List findAllByParticipantIdsContaining(List participantIds); // save 메서드는 이미 몽고디비에 존재하므로 재 정의 할 필요 X diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberRepository.java index 801f871..4a23932 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberRepository.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MemberRepository.java @@ -6,35 +6,18 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.time.LocalDate; import java.util.List; +import java.util.Optional; //@Repository //Component가 들어가있다. +@Repository public interface MemberRepository extends JpaRepository { -// @PersistenceContext //여기에 JPA의 em를 자기가 주입을 해줘서 스프링이 알아서 해준다. -// private EntityManager em; //이 엔티티매니저를 이렇게 해주면 스프링이 엔티티 매니져를 만들어서 주입해준다. - -// //맴버 저장 -// public void save(Member member) { -// em.persist(member); -// } -// -// // ID로 찾기 -// public Member findById(int id) { -// return em.find(Member.class, id); -// } -// -// // 모든 맴버 찾기 -// public List findAll() { -// return em.createQuery("select m from Member m", Member.class) -// .getResultList(); -// } -// -// // 이름으로 찾기 -// public List findByName(String nickname) { -// return em.createQuery("select m from Member m where m.nickname = :nickname", Member.class) -// .setParameter("nickname", nickname) -// .getResultList(); -// } + Optional findByEmail(String email); + Optional findById(Integer id); + List findByNicknameContaining(String searchValue); + Optional findByNickname(String nickname); + List findAllByBirthday(LocalDate date); } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MissionRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MissionRepository.java new file mode 100644 index 0000000..7995167 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/MissionRepository.java @@ -0,0 +1,11 @@ +package com.todoslave.feedme.repository; +import com.todoslave.feedme.domain.entity.mission.Mission; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface MissionRepository extends JpaRepository { + List findByWeatherCondition(String weatherCondition); +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/RefreshTokenRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/RefreshTokenRepository.java new file mode 100644 index 0000000..bfae470 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/RefreshTokenRepository.java @@ -0,0 +1,15 @@ +package com.todoslave.feedme.repository; + +import com.todoslave.feedme.login.dto.RefreshToken; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface RefreshTokenRepository extends CrudRepository { + + // accessToken으로 RefreshToken을 찾아온다. + Optional findByAccessToken(String accessToken); + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TestRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TestRepository.java deleted file mode 100644 index c4223a4..0000000 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TestRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.todoslave.feedme.repository; - -public class TestRepository { -} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TodoCategoryRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TodoCategoryRepository.java new file mode 100644 index 0000000..5aad93e --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TodoCategoryRepository.java @@ -0,0 +1,13 @@ +package com.todoslave.feedme.repository; + +import com.todoslave.feedme.domain.entity.task.TodoCategory; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TodoCategoryRepository extends JpaRepository { + + List findAllByMemberId(int memberId); + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TodoRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TodoRepository.java index c8b7e1b..63757f2 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TodoRepository.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TodoRepository.java @@ -1,9 +1,12 @@ package com.todoslave.feedme.repository; import com.todoslave.feedme.domain.entity.task.Todo; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -13,6 +16,16 @@ public interface TodoRepository extends JpaRepository { @Query("SELECT t FROM Todo t WHERE t.member.id = :memberId AND FUNCTION('DATE', t.createdAt) = :createdAt") - List findAllByMemberIdAndCreatedAt(@Param("memberId") int memberId, @Param("createdAt") Date createdAt); + List findAllByMemberIdAndCreatedAt(@Param("memberId") int memberId, @Param("createdAt") LocalDate createdAt); + + @Query("SELECT t FROM Todo t WHERE t.member.id = :memberId AND FUNCTION('DATE', t.createdAt) = :createdAt AND t.isCompleted = :isCompleted") + List findAllByMemberIdAndCreatedAtIsCompleted(@Param("memberId") int memberID, @Param("createdAt") LocalDate createdAt,@Param("isCompleted") int isCompleted); + + @Query("SELECT t.member.id FROM Todo t WHERE t.createdAt = :createdAt AND t.isCompleted = 0") + List findMemberIdAllByCreatedAtAndIsCompleted(LocalDate createdAt); + + @Query("SELECT COUNT(t) FROM Todo t WHERE t.createdAt = :date AND t.isCompleted = :isCompleted") + long countTodoByDateAndIsCompleted(@Param("date") LocalDate date, @Param("isCompleted") int isCompleted); + List findByMemberIdAndCreatedAt(int memberId, LocalDate createdAt); } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TokenBlacklistRepository.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TokenBlacklistRepository.java new file mode 100644 index 0000000..7083dbf --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/repository/TokenBlacklistRepository.java @@ -0,0 +1,10 @@ +package com.todoslave.feedme.repository; + +import com.todoslave.feedme.login.TokenBlacklist; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface TokenBlacklistRepository extends JpaRepository { + Optional findByToken(String token); +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/AlarmService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/AlarmService.java index ba5baa7..534b01a 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/AlarmService.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/AlarmService.java @@ -1,5 +1,14 @@ package com.todoslave.feedme.service; +import com.todoslave.feedme.domain.entity.check.Alarm; +import com.todoslave.feedme.domain.entity.membership.Member; +import java.time.LocalDateTime; + public interface AlarmService { + void todoCompleted(); + boolean requestFriendship(Member requestor, Member counterpart); + void congratsBirthday(); + void checkAlarm(); + } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/AlarmServiceImpl.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/AlarmServiceImpl.java index ea4f6f4..ab5afa0 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/AlarmServiceImpl.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/AlarmServiceImpl.java @@ -1,14 +1,108 @@ package com.todoslave.feedme.service; +import com.todoslave.feedme.domain.entity.check.Alarm; +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.domain.entity.membership.MemberAlarm; +import com.todoslave.feedme.domain.entity.task.Todo; +import com.todoslave.feedme.event.AlarmCreatedEvent; import com.todoslave.feedme.repository.AlarmRepository; +import com.todoslave.feedme.repository.MemberAlarmRepository; +import com.todoslave.feedme.repository.MemberRepository; +import com.todoslave.feedme.repository.TodoRepository; +import java.sql.Time; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Date; +import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.scheduling.annotation.Scheduled; @RequiredArgsConstructor public class AlarmServiceImpl implements AlarmService{ private AlarmRepository alarmRepository; + private MemberAlarmRepository memberAlarmRepository; + private TodoRepository todoRepository; + private MemberRepository memberRepository; + private ApplicationEventPublisher eventPublisher; + + //1시간 마다 확인, 일정 완료 했는지 여부 + @Scheduled(cron = "0 0 * * * ?") + public void todoCompleted(){ + + LocalDate currentDay = LocalDate.now(); + List members = todoRepository.findMemberIdAllByCreatedAtAndIsCompleted(currentDay); + + for(int memberId : members){ + + LocalTime currentTime = LocalTime.now(); + int time = currentTime.getHour(); + + if(memberAlarmRepository.existsByMemberIdAndAlarmTime(memberId,time)) { + + Alarm alarm = new Alarm(); + + Member member = new Member(); + member.setId(memberId); + + alarm.setMember(member); + alarm.setContent("(크리쳐 이름)"+"이 기다리고 있어요!"); + alarmRepository.save(alarm); + + eventPublisher.publishEvent(new AlarmCreatedEvent(this, alarm,"Todo")); + } + } + } + + //친구 요청 알림 + public boolean requestFriendship(Member requestor,Member counterpart){ + + Alarm alarm = new Alarm(); + + alarm.setMember(counterpart); + alarm.setContent(requestor.getNickname()+"님이 친구 요청을 보내셨습니다."); + + Alarm a = alarmRepository.save(alarm); + + if(a==null){ + return false; + } + + eventPublisher.publishEvent(new AlarmCreatedEvent(this, alarm,"Friend")); + + return true; + } + + //생일 알림 + @Scheduled(cron = "0 0 0 * * *") + public void congratsBirthday(){ + + LocalDate date = LocalDate.now(); + List birthdayPerson = memberRepository.findAllByBirthday(date); + + Alarm alarm = new Alarm(); + + for(Member member : birthdayPerson){ + + alarm.setMember(member); + alarm.setContent(member.getNickname()+"님! 생일 축하합니다!"); + + alarmRepository.save(alarm); + + eventPublisher.publishEvent(new AlarmCreatedEvent(this, alarm,"Birthday")); + + } + + } + + @Override + public void checkAlarm() { + + } } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureService.java new file mode 100644 index 0000000..44b85f8 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureService.java @@ -0,0 +1,25 @@ +package com.todoslave.feedme.service; + + +import com.todoslave.feedme.DTO.CreatureInfoResponseDTO; +import com.todoslave.feedme.domain.entity.avatar.Creature; +import org.springframework.web.multipart.MultipartFile; + +import javax.swing.*; + +public interface CreatureService { + + + //크리쳐 만들기 + Creature createFristCreature(String keyword, String photo, String creatureName); + //크리쳐 정보 가져오기 + CreatureInfoResponseDTO CreatureInfo(); + //크리쳐 삭제하기 + boolean removeCreature(); + //크리쳐 경험치 올리기 + void expUp(int toDoCnt); + //크리쳐 레벨업 + void LevelUp(); + + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureServiceImpl.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureServiceImpl.java new file mode 100644 index 0000000..f80b6a7 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureServiceImpl.java @@ -0,0 +1,147 @@ +package com.todoslave.feedme.service; + +import com.todoslave.feedme.DTO.CreatureInfoResponseDTO; +import com.todoslave.feedme.domain.entity.avatar.Creature; +import com.todoslave.feedme.domain.entity.membership.Emotion; +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.login.util.SecurityUtil; +import com.todoslave.feedme.repository.CreatureRepository; +import com.todoslave.feedme.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.swing.*; +import java.time.LocalDate; +import java.time.Period; + +@Service +@RequiredArgsConstructor +public class CreatureServiceImpl implements CreatureService { + + final private MemberRepository memberRepository; + + final private CreatureRepository creatureRepository; + + //크리쳐 만들기 + @Override + public Creature createFristCreature(String keyword, String photo, String creatureName) { + //멤버 가져오고 + Member member = SecurityUtil.getCurrentMember(); + //크리쳐 만들고 + Creature creature = new Creature(); + //이름 설정하고 + creature.setCreatureName(creatureName); + //멤버와 매핑 시켜주고 + creature.setMember(member); + //경험치와 레벨은 자동 0으로 설정 + + creatureRepository.save(creature); //저장 + + //여기서 사진 만들라고 명령 내리시고!!!!!!!!!!!!!!!!!!!!!! + + return creature; + } + + // 크리쳐 보기 + @Override + public CreatureInfoResponseDTO CreatureInfo() { + CreatureInfoResponseDTO creatureInfoResponseDTO = new CreatureInfoResponseDTO(); + Member member = SecurityUtil.getCurrentMember(); + creatureInfoResponseDTO.setName(member.getCreature().getCreatureName()); + creatureInfoResponseDTO.setExp(member.getCreature().getExp()); + creatureInfoResponseDTO.setImg(generateCreatureImgPath(member)); + + // 현재 날짜 가져오기 + LocalDate currentDate = LocalDate.now(); + // 가입 날짜 가져오기 (Timestamp를 LocalDate로 변환) + LocalDate joinDate = member.getJoinDate().toLocalDate(); + // 가입한 날로부터 며칠째인지 계산 + int daysSinceJoin = Period.between(joinDate, currentDate).getDays(); + + // day 설정 + creatureInfoResponseDTO.setDay(daysSinceJoin); + + return creatureInfoResponseDTO; + } + + //크리쳐 삭제(재발급을 위한) + @Override + public boolean removeCreature() { + Member member = SecurityUtil.getCurrentMember(); + Creature creature = member.getCreature(); + creatureRepository.delete(creature); + member.setCreature(null); + memberRepository.save(member); + return true; + } + + //크리쳐 성장 + @Override + public void expUp(int toDoCnt) { + + Creature creature = SecurityUtil.getCurrentMember().getCreature(); + + System.out.println(creature.toString()); + + // 하루 오를 수 있는 최대한의 경험치 제한 + if (toDoCnt > 7) { + toDoCnt = 7; + } + + int nowExp = creature.getExp() + toDoCnt; + + // 현재 레벨에 따른 경험치와 레벨업 조건 처리 + switch (creature.getLevel()) { + case 0: // 알 + System.out.println("여기 들어가니?"); + if (nowExp >= 10) { // 레벨업 조건 + creature.setLevel(1); + creature.setExp(nowExp - 10); + } else { + + System.out.println("여기 들어가야 하는데"); + System.out.println(nowExp); + creature.setExp(nowExp); + } + break; + case 1: // 1레벨 + if (nowExp >= 30) { // 레벨업 조건 + creature.setLevel(2); + creature.setExp(nowExp - 30); + } else { + creature.setExp(nowExp); + } + break; + case 2: // 2레벨 + if (nowExp >= 100) { // 레벨업 조건 + creature.setLevel(3); + creature.setExp(nowExp - 100); + } else { + creature.setExp(nowExp); + } + break; + case 3: // 3레벨 + default: + creature.setExp(nowExp); + break; + } + creatureRepository.save(creature); + } + + //크리쳐 레벨업 + @Override + public void LevelUp() { + Creature creature = SecurityUtil.getCurrentMember().getCreature(); + creature.setLevel(creature.getLevel()+1); + } + + //크리쳐 이미지 주소 + private String generateCreatureImgPath(Member member) { + Creature creature = member.getCreature(); + int creatureLevel = creature.getLevel(); + int creatureId = creature.getId(); + return "http://localhost:8080/image/creature/" + creatureId + "_" +creatureLevel; + } +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureTodoService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureTodoService.java new file mode 100644 index 0000000..9d453c6 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureTodoService.java @@ -0,0 +1,28 @@ +package com.todoslave.feedme.service; + +import com.todoslave.feedme.DTO.*; + +import java.time.LocalDate; +import java.util.List; + +public interface CreatureTodoService { + + // 크리쳐 일정 생성 (날씨 영향) + List insertTodo(String weather); + + // 해당 날의 일정 가져오기 + List getCreatureTodoCalendarDaily(CretureTodoRequestDTO cretureTodoRequestDTO); + + // 내일날 어제날 가져올 수 있게! + List getCreatureTodoListDaily(CreatureTodoDailyRequestDTO creatureTodoDailyRequestDTO); + + // 일정 하나 완료/취소 하기 + CreatureTodoResponseDTO completeTodo(int CreaturetoId); + + // 메인화면에서 당일 안한 일정들 불러오기 (할일 목록에서 일정 불러오기) + List getCreatureTodoMainDaily(); + + + + +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureTodoServiceImpl.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureTodoServiceImpl.java new file mode 100644 index 0000000..0002eb6 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/CreatureTodoServiceImpl.java @@ -0,0 +1,158 @@ +package com.todoslave.feedme.service; + +import com.todoslave.feedme.DTO.CreatureTodoDailyRequestDTO; +import com.todoslave.feedme.DTO.CreatureTodoResponseDTO; +import com.todoslave.feedme.DTO.CretureTodoRequestDTO; +import com.todoslave.feedme.DTO.TodoRequestDTO; +import com.todoslave.feedme.domain.entity.mission.Mission; +import com.todoslave.feedme.domain.entity.task.CreatureTodo; +import com.todoslave.feedme.login.util.SecurityUtil; +import com.todoslave.feedme.repository.CreatureTodoReposito; +import com.todoslave.feedme.repository.MissionRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.sql.Timestamp; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +@Service +@RequiredArgsConstructor +public class CreatureTodoServiceImpl implements CreatureTodoService{ + + final private MissionRepository missionRepository; + final private CreatureTodoReposito creatureTodoRepository; + private Random random = new Random(); + // 로그인시에 없으면 생성 + + @Override + public List insertTodo(String weather) { + + int memberId = SecurityUtil.getCurrentMember().getId(); + LocalDate today = LocalDate.now(); + List todayTodos = creatureTodoRepository.findByMemberIdAndCreatedAt(memberId, today); + + if (!todayTodos.isEmpty()) { + return null; // 오늘의 미션이 이미 생성되었으면 null 반환 + } + + + List missions = missionRepository.findByWeatherCondition(weather); + + if (missions.isEmpty()) { + missions = missionRepository.findByWeatherCondition("default"); + } + + System.out.println(missions.size()+"감자테"); + Mission selectedMission = missions.get(random.nextInt(missions.size())); + + List Dmissions= missionRepository.findByWeatherCondition("default"); + Mission selectedDMission = Dmissions.get(random.nextInt(Dmissions.size())); + + CreatureTodo creatureTodo1 = new CreatureTodo(); + creatureTodo1.setContent(selectedMission.getMission()); + creatureTodo1.setMember(SecurityUtil.getCurrentMember()); + + CreatureTodo creatureTodo2 = new CreatureTodo(); + creatureTodo2.setContent(selectedDMission.getMission()); + creatureTodo2.setMember(SecurityUtil.getCurrentMember()); + + creatureTodoRepository.save(creatureTodo1); + creatureTodoRepository.save(creatureTodo2); + + // ResponseDTO 리스트 생성 + List responseDTOList = new ArrayList<>(); + responseDTOList.add(toResponseDTO(creatureTodo1)); + responseDTOList.add(toResponseDTO(creatureTodo2)); + + return responseDTOList; + } + + //DTO 변환 + private CreatureTodoResponseDTO toResponseDTO(CreatureTodo creatureTodo) { + return CreatureTodoResponseDTO.builder() + .id(creatureTodo.getId()) + .content(creatureTodo.getContent()) + .createdAt(creatureTodo.getCreatedAt()) + .isCompleted(creatureTodo.getIsCompleted()) + .build(); + } + + //해당일 정보 가져오기 + @Override + public List getCreatureTodoCalendarDaily(CretureTodoRequestDTO cretureTodoRequestDTO) { + + int memberId = SecurityUtil.getCurrentMember().getId(); + List todayTodos = creatureTodoRepository.findByMemberIdAndCreatedAt(memberId, cretureTodoRequestDTO.getDate()); + + List responseDTOList = new ArrayList<>(); + for (CreatureTodo creatureTodo : todayTodos) { + responseDTOList.add(toResponseDTO(creatureTodo)); + } + return responseDTOList; + } + + + //일 하나 완료하기 + @Override + public CreatureTodoResponseDTO completeTodo(int CreaturetoId) { + // 나중가서 ㅇㅋ 취소는 할 수 있음 단 경험치는 안줌 + CreatureTodo creatureTodo = creatureTodoRepository.findById(CreaturetoId).orElse(null); + + if (creatureTodo.getIsCompleted()==1) { + creatureTodo.setIsCompleted(0); + }else{ + creatureTodo.setIsCompleted(1); + } + creatureTodoRepository.save(creatureTodo); + return toResponseDTO(creatureTodo); + } + + //오늘 안한것만 당겨오게 + @Override + public List getCreatureTodoMainDaily() { + LocalDate today = LocalDate.now(); + int memberId = SecurityUtil.getCurrentMember().getId(); + List todayTodos = creatureTodoRepository.findByMemberIdAndCreatedAt(memberId, today); + + List responseDTOList = new ArrayList<>(); + + for (CreatureTodo creatureTodo : todayTodos) { + if (creatureTodo.getIsCompleted()==0) { + responseDTOList.add(toResponseDTO(creatureTodo)); + } + } + + return responseDTOList; + } + + //매서드 이름 바꿨네 + @Override + public List getCreatureTodoListDaily(CreatureTodoDailyRequestDTO creatureTodoDailyRequestDTO) { + + //현재 날자 + LocalDate date = creatureTodoDailyRequestDTO.getDate(); + //멤버 + int memberId = SecurityUtil.getCurrentUserId(); + //누른 버튼 + if(creatureTodoDailyRequestDTO.getNext() < 0 ){ //-1 일때 + date.minusDays(1); + }else{ + date.plusDays(1); + } + + + //크리쳐 투두 내가 만들거 싹 가져와!! + List todayTodos = creatureTodoRepository.findByMemberIdAndCreatedAt(memberId, date); + List responseDTOList = new ArrayList<>(); + + //바꿔서 보내줘! + for (CreatureTodo creatureTodo : todayTodos) { + responseDTOList.add(toResponseDTO(creatureTodo)); + } + + return responseDTOList; + } +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/DayOffService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/DayOffService.java new file mode 100644 index 0000000..904fb75 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/DayOffService.java @@ -0,0 +1,41 @@ +package com.todoslave.feedme.service; + + +import com.todoslave.feedme.domain.entity.task.DayOff; +import com.todoslave.feedme.repository.DayOffRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +@Service +public class DayOffService { + + private final DayOffRepository dayOffRepository; + + + @Autowired + public DayOffService(DayOffRepository dayOffRepository) { + this.dayOffRepository = dayOffRepository; + } + //맴버 아이디랑 날자로 찾기 + public Optional findDayOffByMemberIdAndDate(int memberId, LocalDate date) { + return dayOffRepository.findByMemberIdAndEndDay(memberId, date); + } + //데이오프 만들기 + public DayOff saveDayOff(DayOff dayOff) { + return dayOffRepository.save(dayOff); + } + + //있는지 검사 + public boolean isActionAllowed(int memberId, LocalDate date) { + // 특정 날짜에 한 개라도 DayOff가 있으면 false 반환 + return dayOffRepository.countByMemberIdAndEndDay(memberId, date) == 0; + } + //쓸일 없어 + public void deleteDayOff(int id) { + dayOffRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedCommentService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedCommentService.java new file mode 100644 index 0000000..1f9b197 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedCommentService.java @@ -0,0 +1,18 @@ +package com.todoslave.feedme.service; + +import com.todoslave.feedme.DTO.*; + +public interface FeedCommentService { + + //댓글 등록 + FeedCommentResponseDTO insertFeedCommend(FeedCommentRequestDTO FeedCommentRequestDTO, int feedid); + + //댓글 수정 + FeedCommentResponseDTO modifyComment(FeedCommentRequestDTO feedCommentRequestDTO, int feedcommantId); + + //댓글 삭제 + boolean delectFeedComment(int feedId); + + + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedCommentServiceImpl.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedCommentServiceImpl.java new file mode 100644 index 0000000..52b6cf7 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedCommentServiceImpl.java @@ -0,0 +1,86 @@ +package com.todoslave.feedme.service; + +import com.todoslave.feedme.DTO.FeedCommentRequestDTO; +import com.todoslave.feedme.DTO.FeedCommentResponseDTO; +import com.todoslave.feedme.domain.entity.Feed.Feed; +import com.todoslave.feedme.domain.entity.Feed.FeedComment; +import com.todoslave.feedme.login.util.SecurityUtil; +import com.todoslave.feedme.repository.FeedCommentRepository; +import com.todoslave.feedme.repository.FeedRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.sql.Date; +import java.time.LocalDateTime; + + +@RequiredArgsConstructor +@Service +public class FeedCommentServiceImpl implements FeedCommentService { + + private final FeedRepository feedRepository; + private final FeedCommentRepository commentRepository; + private final FeedCommentRepository feedCommentRepository; + + //피드 댓글 작성 + @Override + public FeedCommentResponseDTO insertFeedCommend(FeedCommentRequestDTO feedCommentRequestDTO, int feedid) { + FeedComment feedComment = new FeedComment(); + Feed feed = feedRepository.findById(feedid).orElse(null); + + if (feed == null) { + // Feed가 존재하지 않으면 예외를 던지거나 적절한 처리를 합니다. + throw new IllegalArgumentException("Feed가 존재하지 않습니다."); + } + + feedComment.setFeed(feed); + feedComment.setMember(SecurityUtil.getCurrentMember()); + feedComment.setContent(feedCommentRequestDTO.getContent()); + + commentRepository.save(feedComment); + + return convertCommentToDTO(feedComment); + } + + + //댓글 수정 + @Override + public FeedCommentResponseDTO modifyComment(FeedCommentRequestDTO feedCommentRequestDTO, int feedcommantId) { + FeedComment feedComment = feedCommentRepository.findById(feedcommantId).orElse(null); + //못찾아 왓을 때 + if (feedComment == null) { + return null; + } + //내가 쓴 글이 아닐 때 + if (SecurityUtil.getCurrentMember().getId() != feedComment.getMember().getId()) { + return null; + } + + feedComment.setContent(feedCommentRequestDTO.getContent()); + feedComment.setCreatedAt(LocalDateTime.now()); + commentRepository.save(feedComment); + return convertCommentToDTO(feedComment); + } + + //댓글 삭제 + @Override + public boolean delectFeedComment(int feedId) { + FeedComment feedComment = feedCommentRepository.findById(feedId).orElse(null); + if (feedComment == null|| SecurityUtil.getCurrentMember().getId() != feedComment.getMember().getId()) { + return false; + } + commentRepository.delete(feedComment); + return true; + } + + + //변환하기 댓글 -> FeedCommentResponseDTO로 + private FeedCommentResponseDTO convertCommentToDTO(FeedComment feedComment) { + FeedCommentResponseDTO dto = new FeedCommentResponseDTO(); + dto.setNickname(feedComment.getMember().getNickname()); // Member 엔티티에서 닉네임 가져오기 + dto.setContent(feedComment.getContent()); + dto.setCreatedAt(feedComment.getCreatedAt()); + return dto; + } + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedService.java index 1f21ea6..dc5732d 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedService.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedService.java @@ -1,5 +1,16 @@ package com.todoslave.feedme.service; +import com.todoslave.feedme.DTO.FeedModifyRequest; +import com.todoslave.feedme.DTO.FeedRequestDTO; +import com.todoslave.feedme.DTO.FeedResponseDTO; + public interface FeedService { + FeedResponseDTO insertFeed(FeedRequestDTO feedRequestDTO); + + FeedResponseDTO modifyFeed(int feedId, FeedModifyRequest feedModifyRequest); + + boolean delectFeed(int feedId); + + } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedServiceImpl.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedServiceImpl.java index f7f1c45..2d31e71 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedServiceImpl.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FeedServiceImpl.java @@ -1,8 +1,108 @@ package com.todoslave.feedme.service; +import com.todoslave.feedme.DTO.FeedModifyRequest; +import com.todoslave.feedme.DTO.FeedRequestDTO; +import com.todoslave.feedme.DTO.FeedResponseDTO; +import com.todoslave.feedme.domain.entity.Feed.Feed; +import com.todoslave.feedme.login.util.SecurityUtil; +import com.todoslave.feedme.repository.FeedRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import java.sql.Time; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + + +@Service +@RequiredArgsConstructor public class FeedServiceImpl implements FeedService{ + private final FeedRepository feedRepository; + + @Override + public FeedResponseDTO insertFeed(FeedRequestDTO feedRequestDTO) { + Feed feed = new Feed(); + feed.setContent(feedRequestDTO.getContent()); + feed.setMember(SecurityUtil.getCurrentMember()); + feed.setDiaryDay(feedRequestDTO.getDiaryDate()); + feed.setNickname(SecurityUtil.getCurrentMember().getNickname()); + + feedRepository.save(feed); + return convertToDTO(feed); + } + + @Override + public FeedResponseDTO modifyFeed(int feedId, FeedModifyRequest feedModifyRequest) { + Feed feed = feedRepository.findById(feedId).orElse(null); + if (feed == null) {return null;} + + feed.setContent(feedModifyRequest.getContent()); + feed.setUpdatedAt(LocalDateTime.now()); + feedRepository.save(feed); + return convertToDTO(feed); + } + + @Override + public boolean delectFeed(int feedId) { + Feed feed = feedRepository.findById(feedId).orElse(null); + if (feed == null) {return false;} + if(SecurityUtil.getCurrentMember().equals(feed.getMember())) { + feedRepository.delete(feed); + return true; + } + return false; + } + + public FeedResponseDTO convertToDTO(Feed feed) { + FeedResponseDTO dto = new FeedResponseDTO(); + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + + dto.setCreatedAt(timestamp); + dto.setId(feed.getId()); + dto.setImg(diaryImgPath(feed)); // 이미지 URL이 엔티티에 없다면 필요에 따라 설정 + dto.setContent(feed.getContent()); + dto.setAuthor(feed.getNickname()); + dto.setLikeCnt(String.valueOf(feed.getLikeCount())); + return dto; + } + + private String diaryImgPath(Feed feed) { + int id = feed.getMember().getId(); + String day = String.valueOf(feed.getDiaryDay()); + return "http://localhost:8080/image/pictureDiary/" + id + "_" + day; + } + +// // Feed 엔티티를 FeedResponseDTO로 변환하는 메서드 +// public FeedResponseDTO convertToDTO(Feed feed) { +// FeedResponseDTO dto = new FeedResponseDTO(); +// dto.setId(feed.getId()); +// dto.setImg("image_url_placeholder"); // 이미지 URL이 엔티티에 없다면 필요에 따라 설정 +// dto.setContent(feed.getContent()); +// dto.setAuthor(feed.getNickname()); +// dto.setLikeCnt(String.valueOf(feed.getLikeCount())); +// return dto; +// } +// +// // 모든 피드를 가져와서 DTO 리스트로 반환하는 메서드 +// public List getAllFeeds() { +// List feeds = feedRepository.findAll(); +// return feeds.stream() +// .map(this::convertToDTO) +// .collect(Collectors.toList()); +// } +// +// // 특정 ID의 피드를 가져와서 DTO로 반환하는 메서드 +// public FeedResponseDTO getFeedById(int id) { +// Feed feed = feedRepository.findById(id) +// .orElseThrow(() -> new IllegalArgumentException("Invalid feed ID: " + id)); +// return convertToDTO(feed); +// } + } + + diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FriendService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FriendService.java index 50107d1..b8ebc89 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FriendService.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FriendService.java @@ -1,23 +1,27 @@ package com.todoslave.feedme.service; +import com.todoslave.feedme.DTO.FriendReqRequestDTO; +import com.todoslave.feedme.DTO.FriendReqResponseDTO; +import com.todoslave.feedme.DTO.FriendResponseDTO; import com.todoslave.feedme.domain.entity.communication.Friend; import com.todoslave.feedme.domain.entity.communication.FriendRequest; +import com.todoslave.feedme.domain.entity.membership.Member; import java.util.List; public interface FriendService { // 친구 추가 - void insertFriend(int memberId, String email); + void requestFriend(FriendReqRequestDTO friendReqRequestDTO); // 친구 삭제 void deleteFriend(int friendId); // 친구 목록 조회 - List getFriends(Integer memberId); + List getFriends(); // 친구 요청 목록 조회 - List getRequestFriend(Integer memberId); + List getRequestFriend(); // 친구 수락 void insertFriendship(int requestId); @@ -25,4 +29,7 @@ public interface FriendService { // 친구 거절 void deleteRequestFriend(int requestId); + //친구인지 확인 + boolean isFriend(int memberId, int friendId); + } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FriendServiceImpl.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FriendServiceImpl.java index b2c9a67..8c34c5e 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FriendServiceImpl.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/FriendServiceImpl.java @@ -1,73 +1,160 @@ -//package com.todoslave.feedme.service; -// -//import com.todoslave.feedme.domain.entity.communication.Friend; -//import com.todoslave.feedme.domain.entity.communication.FriendRequest; -//import com.todoslave.feedme.domain.entity.membership.Member; -//import com.todoslave.feedme.repository.FriendRepository; -//import com.todoslave.feedme.repository.FriendRequestRepository; -//import com.todoslave.feedme.repository.MemberRepository; -//import jakarta.persistence.EntityNotFoundException; -//import jakarta.transaction.Transactional; -//import lombok.RequiredArgsConstructor; -// -//import java.util.List; -// -//@RequiredArgsConstructor -//public class FriendServiceImpl implements FriendService{ -// -// MemberRepository memberRepository; -// FriendRepository friendRepository; -// FriendRequestRepository friendRequestRepository; -// -// @Override -// public void insertFriend(int memberId, String email) { -// -// int counterId; -// -// FriendRequest friendRequest = new FriendRequest(); -// friendRequest.setMember(memberId); -// friendRequest.setCounterpart_id(counterId); -// -// friendRequestRepository.save(friendRequest); -// -// } -// -// @Override -// @Transactional -// public void deleteFriend(int friendId) { -// -// friendRequestRepository.deleteById(friendId); -// -// } -// -// @Override -// public List getFriends(Integer memberId) { -// return friendRepository.findAllByMemberId(memberId); -// } -// -// @Override -// public List getRequestFriend(Integer memberId) { -// return friendRequestRepository.findAllByMemberId(memberId); -// } -// -// @Override -// public void insertFriendship(int id) { -// -// FriendRequest friendRequest = friendRequestRepository.findById(id); -// -// Friend friend = new Friend(); -// Member member = friendRequest.getMember(); -// friend.setMember(member); -// friend.setCounterpart_id(friendRequest.getCounterpart_id()); -// -// friendRepository.save(friend); -// -// } -// -// @Override -// public void deleteRequestFriend(int requestId) { -// -// friendRequestRepository.deleteById(requestId); -// -// } -//} +package com.todoslave.feedme.service; +//맴버 채팅 주석 + +import com.todoslave.feedme.domain.entity.communication.Friend; +import com.todoslave.feedme.domain.entity.communication.FriendRequest; +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.repository.FriendRepository; +import com.todoslave.feedme.repository.FriendRequestRepository; +import com.todoslave.feedme.repository.MemberRepository; +import jakarta.persistence.EntityNotFoundException; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Service; +import com.todoslave.feedme.DTO.FriendReqRequestDTO; +import com.todoslave.feedme.DTO.FriendReqResponseDTO; +import com.todoslave.feedme.DTO.FriendResponseDTO; +import com.todoslave.feedme.domain.entity.communication.Friend; +import com.todoslave.feedme.domain.entity.communication.FriendRequest; +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.login.util.SecurityUtil; +import com.todoslave.feedme.repository.FriendRepository; +import com.todoslave.feedme.repository.FriendRequestRepository; +import jakarta.transaction.Transactional; +import java.util.ArrayList; +import lombok.RequiredArgsConstructor; +import java.util.List; + +@RequiredArgsConstructor +@Service +public class FriendServiceImpl implements FriendService{ + + MemberService memberService; + @Autowired + FriendRepository friendRepository; + @Autowired + FriendRequestRepository friendRequestRepository; +// MemberChatService memberChatService; + + // 친구 요청 + @Override + public void requestFriend(FriendReqRequestDTO friendReqRequestDTO) { + + Member member = SecurityUtil.getCurrentMember(); + Member counterpart = memberService.findByNickname(friendReqRequestDTO.getCounterpartNickname()); + + FriendRequest friendRequest = new FriendRequest(); + + friendRequest.setMember(member); + friendRequest.setCounterpartId(counterpart); + + friendRequestRepository.save(friendRequest); + + } + + // 친구 삭제 + @Override + @Transactional + public void deleteFriend(int friendId) { + + friendRequestRepository.deleteById(friendId); + + } + + +// @Override +// public List getFriends(Integer memberId) { +// return friendRepository.findAllByMemberId(memberId); +// } + + +// @Override +// public List getRequestFriend(Integer memberId) { +// return friendRequestRepository.findAllByMemberId(memberId); +// } + + + // 친구 인지 확인하기 + public boolean isFriend(int memberId, int friendId) { + return friendRepository.existsByMemberIdAndCounterpartId(memberId, friendId) || + friendRepository.existsByCounterpartIdAndMemberId(friendId, memberId); + } + + // 친구 목록 불러오기 + @Override + public List getFriends() { + + int memberId = SecurityUtil.getCurrentUserId(); + + List f = friendRepository.findAllByMemberId(memberId); + List friends = new ArrayList<>(); + + for(Friend friend : f){ + + Member counterpart = friend.getCounterpart(); + FriendResponseDTO friendResponseDTO = new FriendResponseDTO(); + friendResponseDTO.setFriendId(friend.getId()); + friendResponseDTO.setCounterpartNickname(counterpart.getNickname()); + friends.add(friendResponseDTO); + + } + + return friends; + } + + // 친구 요청 불러오기 + @Override + public List getRequestFriend() { + + int memberId = SecurityUtil.getCurrentUserId(); + + List friendRequests = friendRequestRepository.findAllByMemberId(memberId); + List friendReqResponseDTOList = new ArrayList<>(); + + for(FriendRequest friendRequest : friendRequests){ + + FriendReqResponseDTO friendReqResponseDTO = new FriendReqResponseDTO(); + friendReqResponseDTO.setId(friendRequest.getId()); + friendReqResponseDTO.setCounterpartNickname(friendRequest.getCounterpartId().getNickname()); + friendReqResponseDTOList.add(friendReqResponseDTO); + + } + + return friendReqResponseDTOList; + } + + // 친구 수락 + @Override + public void insertFriendship(int requestId) { + + FriendRequest friendRequest = friendRequestRepository.findById(requestId); + + Friend friend = new Friend(); + friend.setMember(SecurityUtil.getCurrentMember()); + friend.setCounterpart(friendRequest.getCounterpartId()); + + friendRequestRepository.deleteById(requestId); + + List members = new ArrayList<>(); + int memberId = SecurityUtil.getCurrentUserId(); + members.add(memberId); + int counterpartId = friendRequest.getCounterpartId().getId(); + members.add(counterpartId); + +// memberChatService.insertChatRoom(members); + friendRepository.save(friend); + + } + + // 친구 거절 + @Override + public void deleteRequestFriend(int requestId) { + + friendRequestRepository.deleteById(requestId); + + } + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberChatService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberChatService.java index 4a12f2b..74b526d 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberChatService.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberChatService.java @@ -1,23 +1,29 @@ -package com.todoslave.feedme.service; +//맴버 채팅 주석 -import com.todoslave.feedme.domain.entity.communication.MemberChatMessage; -import com.todoslave.feedme.domain.entity.communication.MemberChatRoom; -import java.util.List; -import org.springframework.data.domain.Slice; -import org.springframework.stereotype.Service; - -@Service -public interface MemberChatService { - - public List getChatRooms(MemberChatRoom room); - - // 채팅방 생성 or 불러오기 - public MemberChatRoom getChatRoom(MemberChatRoom room); - - // 채팅방 메세지 불러오기 - public Slice getChatMessage(String roomId, int page, int size); - - // 채팅방 메세지 저장 - public MemberChatMessage insertChatMessage(MemberChatMessage message); - -} \ No newline at end of file +//package com.todoslave.feedme.service; +// +//import com.todoslave.feedme.DTO.ChatFriendCreateDTO; +//import com.todoslave.feedme.DTO.ChatFriendFindDTO; +//import com.todoslave.feedme.DTO.ChatMessageRequestDTO; +//import com.todoslave.feedme.DTO.ChatMessageResponseDTO; +//import com.todoslave.feedme.domain.entity.communication.MemberChatMessage; +//import com.todoslave.feedme.domain.entity.communication.MemberChatRoom; +//import java.util.List; +//import org.springframework.data.domain.Slice; +//import org.springframework.stereotype.Service; +// +//@Service +//public interface MemberChatService { +// +// public List getChatRooms(ChatFriendFindDTO chatFriendFindDTO); +// +// // 채팅방 생성 or 불러오기 +// public MemberChatRoom getChatRoom(ChatFriendCreateDTO chatFriendCreateDTO); +// +// // 채팅방 메세지 불러오기 +// public Slice getChatMessage(String roomId, int page, int size); +// +// // 채팅방 메세지 저장 +// public ChatMessageResponseDTO insertChatMessage(String roomId, ChatMessageRequestDTO chatMessageRequestDTO); +// +//} \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberChatServiceImpl.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberChatServiceImpl.java index b876445..afa0773 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberChatServiceImpl.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberChatServiceImpl.java @@ -1,61 +1,106 @@ -package com.todoslave.feedme.service; - -import com.todoslave.feedme.domain.entity.communication.MemberChatMessage; -import com.todoslave.feedme.domain.entity.communication.MemberChatRoom; -import com.todoslave.feedme.repository.MemberChatMessageRepository; -import com.todoslave.feedme.repository.MemberChatRoomRepository; -import java.util.ArrayList; -import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.stereotype.Service; - -@Service -public class MemberChatServiceImpl implements MemberChatService{ - - @Autowired - private MemberChatMessageRepository messageRepository; - - @Autowired - private MemberChatRoomRepository roomRepository; - - @Override - public List getChatRooms(MemberChatRoom room) { - List rooms = roomRepository.findAllByParticipantIdsContaining(room.getParticipantIds()); - System.out.println(rooms); - return rooms; - } - - public MemberChatRoom getChatRoom(MemberChatRoom room){ - - MemberChatRoom result = roomRepository.findByParticipantIdsContainingAll(room.getParticipantIds()); - - System.out.println(room); - - if(result==null){ - result = roomRepository.save(room); - } - - System.out.println(result); - - return result; - } - - public Slice getChatMessage(String roomId, int skip, int limit){ - - System.out.println("receive message? service"); - Pageable pageable = PageRequest.of(skip / limit, limit); - - Slice messages = messageRepository.findByMemberChatRoomIdOrderByTransmitAtDesc(roomId, pageable); - - return messages; - } - - public MemberChatMessage insertChatMessage(MemberChatMessage message) { - System.out.println(message); - return messageRepository.save(message); - } - -} +//맴버 채팅 주석 + +//package com.todoslave.feedme.service; +// +//import com.todoslave.feedme.DTO.ChatFriendCreateDTO; +//import com.todoslave.feedme.DTO.ChatFriendFindDTO; +//import com.todoslave.feedme.DTO.ChatMessageRequestDTO; +//import com.todoslave.feedme.DTO.ChatMessageResponseDTO; +//import com.todoslave.feedme.domain.entity.communication.MemberChatMessage; +//import com.todoslave.feedme.domain.entity.communication.MemberChatRoom; +//import com.todoslave.feedme.domain.entity.membership.Member; +//import com.todoslave.feedme.repository.MemberChatMessageRepository; +//import com.todoslave.feedme.repository.MemberChatRoomRepository; +//import com.todoslave.feedme.repository.MemberRepository; +//import java.util.ArrayList; +//import java.util.List; +//import lombok.RequiredArgsConstructor; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.data.domain.PageRequest; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.domain.Slice; +//import org.springframework.stereotype.Service; +// +//@Service +//@RequiredArgsConstructor +//public class MemberChatServiceImpl implements MemberChatService{ +// +// private final MemberChatMessageRepository messageRepository; +// private final MemberChatRoomRepository roomRepository; +// private final MemberRepository memberRepository; + + + + + + + + + +// @Override +// public List getChatRooms(ChatFriendFindDTO chatFriendFindDTO) { +// List rooms = roomRepository.findAllByParticipantIdsContaining(chatFriendFindDTO.getMemberId()); +// +// int counterpartId = -1; +// +// for(MemberChatRoom room : rooms){ +// +// List members = room.getParticipantIds(); +// +// for(Integer member : members){ +// if(chatFriendFindDTO.getMemberId().get(0)==member){ +// continue; +// } +// counterpartId = member; +// } +// +//// Member member = memberRepository.findById(counterpartId); +// +// +// +// } +// +// } +// +// public MemberChatRoom getChatRoom(ChatFriendCreateDTO chatFriendCreateDTO){ +// +// result = roomRepository.save(chatFriendCreateDTO.getMembers()); +// +// return result; +// } + +//import com.todoslave.feedme.domain.entity.communication.MemberChatMessage; +//import org.springframework.data.domain.PageRequest; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.domain.Slice; +// +//public Slice getChatMessage(String roomId, int skip, int limit){ +// +// System.out.println("receive message? service"); +// Pageable pageable = PageRequest.of(skip / limit, limit); +// +// Slice messages = messageRepository.findByMemberChatRoomIdOrderByTransmitAtDesc(roomId, pageable); +// +// return messages; +// } +// +// public ChatMessageResponseDTO insertChatMessage(String roomId, ChatMessageRequestDTO chatMessageRequestDTO) { +// MemberChatMessage memberChatMessage = new MemberChatMessage(); +// +// int memberId = -1; +// +// memberChatMessage.setMemberChatRoomId(roomId); +// memberChatMessage.setContent(chatMessageRequestDTO.getMessage()); +// memberChatMessage.setSendId(memberId); +// +// memberChatMessage = messageRepository.save(memberChatMessage); +// ChatMessageResponseDTO response = new ChatMessageResponseDTO(); +// +// response.setMessage(memberChatMessage.getContent()); +// response.setTransmitAt(memberChatMessage.getTransmitAt()); +//// response.getSendNickname() = (대충 시큐리티에서 닉네임 받아오는 메서드); +// +// return response; +// } +// +//} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberService.java index cd39e11..146aa37 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberService.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MemberService.java @@ -1,28 +1,44 @@ package com.todoslave.feedme.service; +import com.todoslave.feedme.DTO.MemberSearchResponseDTO; +import com.todoslave.feedme.DTO.MemberSignupRequestDTO; +import com.todoslave.feedme.DTO.MypageResponseDTO; +import com.todoslave.feedme.domain.entity.avatar.Creature; +import com.todoslave.feedme.domain.entity.membership.Emotion; import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.login.util.SecurityUtil; import com.todoslave.feedme.repository.MemberRepository; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.Period; +import java.time.ZoneId; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; @Service -@Transactional(readOnly = true) //조회에선 +@Transactional +//@Transactional(readOnly = true) //조회에선 @RequiredArgsConstructor // 생성자 만들어 주는 얘 public class MemberService { - // @Autowired - private final MemberRepository memberRepository; + @Autowired + MemberRepository memberRepository; - // 회원가입 - @Transactional //가입시에 - public int Join(Member member) { -// sameNameCheck(member); // 중복 회원 검증 - memberRepository.save(member); - return member.getId(); + @Autowired + FriendService friendService; + + + //그냥 가입 시켜주는 얘 + public Member insertMember (Member member) { + return memberRepository.save(member); } // 회원 전체 조회 @@ -30,18 +46,141 @@ public List findMembers() { return memberRepository.findAll(); } - // 단수 조회 -// public Member findOne(int id) { -// return memberRepository.findById(id); -// } + //아이디로 맴버 찾기 + public Member findById(int userId) { + return memberRepository.findById(userId) + .orElseThrow(() -> new RuntimeException("Member not found by id: " + userId)); + } + + //이메일로 찾기 + public Optional findByEmail(String email) { + return memberRepository.findByEmail(email); + } + + // 닉네임으로 맴버 찾기 + public Member findByNickname(String Nickname) { + return memberRepository.findByNickname(Nickname).orElse(null); + } + + public boolean authenticate(String email) { + return memberRepository.findByEmail(email).isPresent(); + } + + //회원가입 + public Member registerMember(MemberSignupRequestDTO memberSignupRequestDTO) { +// memberRepository.findByEmail(memberSignup.getEmail()).orElseThrow(() -> new RuntimeException("Member not found by email: " + memberSignup.getEmail())); + + Member member = new Member(); + member.setEmail(memberSignupRequestDTO.getEmail()); + member.setBirthday(memberSignupRequestDTO.getBirthday()); + member.setNickname(memberSignupRequestDTO.getNickname()); + member.setUserRole(memberSignupRequestDTO.getUserRole()); + + memberRepository.save(member); + return member; + } + + public Member updateMember(MemberSignupRequestDTO memberSignupRequestDTO) { + int id = SecurityUtil.getCurrentUserId(); + + Member member = memberRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("사용자를 찾을 수 없습니다.")); + + member.setNickname(memberSignupRequestDTO.getNickname()); + member.setBirthday(memberSignupRequestDTO.getBirthday()); + member.setUserRole(memberSignupRequestDTO.getUserRole()); + + return memberRepository.save(member); + } + + public boolean removeMember() { + Member member = SecurityUtil.getCurrentMember(); + + if (member == null) { + return false; + } + memberRepository.delete(member); + return true; + } + + //닉네임에 맞는 친구 데려오기 + public List getMemberList(String searchvalue) { + + List members = memberRepository.findByNicknameContaining(searchvalue); + List memberSerachResponse = new ArrayList<>(); - // 중복 체크 -// private void sameNameCheck(Member member) { -// List findMembers = memberRepository.findByName(member.getNickname()); -// if(!findMembers.isEmpty()) { -// throw new IllegalStateException("중복 닉네임"); -// } -// } + for (Member member : members) { + System.out.println("하하"); + System.out.println(member.getId()); + System.out.println(SecurityUtil.getCurrentMember().getId()); + MemberSearchResponseDTO mem = new MemberSearchResponseDTO(); + mem.setNickname(member.getNickname()); + + mem.setCreatureImg(generateCreatureImgPath(member)); + + if(member.getId()== SecurityUtil.getCurrentUserId()){continue;} + + if(friendService.isFriend(member.getId(), SecurityUtil.getCurrentUserId())){ + + mem.setFriend(true); + + }else { + + mem.setFriend(false); + //만약 친구이면 true 아니면 false + } + memberSerachResponse.add(mem); + } + + return memberSerachResponse; + } + + private String generateCreatureImgPath(Member member) { + Creature creature = member.getCreature(); + int creatureLevel = creature.getLevel(); + int creatureId = creature.getId(); + return "http://localhost:8080/image/creature/" + creatureId + "_" +creatureLevel; + } + + public MypageResponseDTO getMyPage() { + Member member = SecurityUtil.getCurrentMember(); + Creature creature = member.getCreature(); + + MypageResponseDTO myPage = new MypageResponseDTO(); + + myPage.setNickname(member.getNickname()); + myPage.setEmail(member.getEmail()); + + + //생일 바꾸기 + Timestamp birthdayTimestamp = member.getBirthday(); + + // Timestamp를 LocalDate로 변환 + LocalDate birthdayLocalDate = birthdayTimestamp.toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDate(); + + // 변환된 LocalDate를 myPage에 설정 + myPage.setBrithday(birthdayLocalDate); + + myPage.setCreatureId(creature.getId()); + myPage.setCreatureName(creature.getCreatureName()); + myPage.setExp(creature.getExp()); + myPage.setLevel(creature.getLevel()); + myPage.setImage(generateCreatureImgPath(member)); + + + LocalDate currentDate = LocalDate.now(); + // 가입 날짜 가져오기 (Timestamp를 LocalDate로 변환) + LocalDate joinDate = member.getJoinDate().toLocalDate(); + // 가입한 날로부터 며칠째인지 계산 + int daysSinceJoin = Period.between(joinDate, currentDate).getDays(); + + myPage.setTogetherDay(daysSinceJoin); + + + return myPage; + } } diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MissionService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MissionService.java new file mode 100644 index 0000000..da67888 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/MissionService.java @@ -0,0 +1,46 @@ +package com.todoslave.feedme.service; + +import com.todoslave.feedme.domain.entity.mission.Mission; +import com.todoslave.feedme.repository.MissionRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Random; + +@Service +public class MissionService { + + @Autowired + private MissionRepository missionRepository; + + private Random random = new Random(); + + public Mission getRandomMissionByIndices(int startIndex, int endIndex) { + List missions = missionRepository.findAll(); + if (missions.isEmpty() || endIndex > missions.size()) { + return null; + } + return missions.get(startIndex - 1 + random.nextInt(endIndex - startIndex + 1)); + } + + public Mission getRainyMission() { + return getRandomMissionByIndices(1, 20); + } + + public Mission getSnowyMission() { + return getRandomMissionByIndices(21, 40); + } + + public Mission getSunnyMission() { + return getRandomMissionByIndices(41, 60); + } + + public Mission getCloudyMission() { + return getRandomMissionByIndices(61, 80); + } + + public Mission getDefaultMission() { + return getRandomMissionByIndices(81, 100); + } +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoCategoryService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoCategoryService.java new file mode 100644 index 0000000..094a712 --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoCategoryService.java @@ -0,0 +1,17 @@ +package com.todoslave.feedme.service; + +import com.todoslave.feedme.DTO.TodoCategoryRequestDTO; +import com.todoslave.feedme.DTO.TodoCategoryResponseDTO; +import java.util.List; + +public interface TodoCategoryService { + + public List getCategories(); + + public TodoCategoryResponseDTO updateCategory(TodoCategoryRequestDTO todoCategoryRequestDTO); + + public void deleteCategory(int id); + + public TodoCategoryResponseDTO insertCategory(String name); + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoCategoryServiceImpl.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoCategoryServiceImpl.java new file mode 100644 index 0000000..8472b0c --- /dev/null +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoCategoryServiceImpl.java @@ -0,0 +1,86 @@ +package com.todoslave.feedme.service; + +import com.todoslave.feedme.DTO.TodoCategoryRequestDTO; +import com.todoslave.feedme.DTO.TodoCategoryResponseDTO; +import com.todoslave.feedme.domain.entity.membership.Member; +import com.todoslave.feedme.domain.entity.task.TodoCategory; +import com.todoslave.feedme.login.util.SecurityUtil; +import com.todoslave.feedme.repository.MemberRepository; +import com.todoslave.feedme.repository.TodoCategoryRepository; +import jakarta.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class TodoCategoryServiceImpl implements TodoCategoryService { + + private TodoCategoryRepository todoCategoryRepository; + private MemberRepository memberRepository; + + // 카테고리들 가져오기 + @Override + public List getCategories() { + + int memberId = SecurityUtil.getCurrentUserId(); + List result = todoCategoryRepository.findAllByMemberId(memberId); + + List categories = new ArrayList<>(); + + for(TodoCategory todoCategory : result){ + + TodoCategoryResponseDTO todoCategoryResponseDTO = new TodoCategoryResponseDTO(); + todoCategoryResponseDTO.setId(todoCategory.getId()); + todoCategoryResponseDTO.setName(todoCategory.getName()); + categories.add(todoCategoryResponseDTO); + + } + + return categories; + } + + // 카테고리 수정 + @Override + @Transactional + public TodoCategoryResponseDTO updateCategory(TodoCategoryRequestDTO todoCategoryRequestDTO) { + + TodoCategory todoCategory = todoCategoryRepository.findById(todoCategoryRequestDTO.getId()).orElseThrow(); + todoCategory.setName(todoCategoryRequestDTO.getName()); + TodoCategoryResponseDTO result = new TodoCategoryResponseDTO(); + result.setName(todoCategory.getName()); + result.setId(todoCategory.getId()); + + return result; + } + + // 카테고리 삭제 + @Override + @Transactional + public void deleteCategory(int id) { + + todoCategoryRepository.deleteById(id); + + } + + // 카테고리 등록 + @Override + public TodoCategoryResponseDTO insertCategory(String name) { + + Member member = SecurityUtil.getCurrentMember(); + + TodoCategory todoCategory = new TodoCategory(); + + todoCategory.setName(name); + todoCategory.setMember(member); + todoCategory = todoCategoryRepository.save(todoCategory); + + TodoCategoryResponseDTO todoCategoryResponseDTO = new TodoCategoryResponseDTO(); + todoCategoryResponseDTO.setId(todoCategory.getId()); + todoCategoryResponseDTO.setName(todoCategory.getName()); + + return todoCategoryResponseDTO; + } + +} diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoService.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoService.java index 39085fd..a8db632 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoService.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoService.java @@ -1,27 +1,45 @@ package com.todoslave.feedme.service; +import com.todoslave.feedme.DTO.TodoCalendarResponseDTO; +import com.todoslave.feedme.DTO.TodoCreateRequestDTO; +import com.todoslave.feedme.DTO.TodoDailyRequestDTO; +import com.todoslave.feedme.DTO.TodoResponseDTO; +import com.todoslave.feedme.DTO.TodoMainResponseDTO; +import com.todoslave.feedme.DTO.TodoModifyRequestDTO; +import com.todoslave.feedme.DTO.TodoRequestDTO; import com.todoslave.feedme.domain.entity.task.Todo; -import java.util.Date; + +import java.time.LocalDate; import java.util.List; public interface TodoService { - // 하루 일정 불러오기 - public List getTodoDaily(int memberId, Date createdAt); + // 할일 목록에서 일정 불러오기 + public List getTodoListDaily(TodoDailyRequestDTO todoDailyRequestDTO); + + // 메인 달력에서 일정 불러오기 + public List getTodoCalendarDaily(TodoRequestDTO todoRequestDTO); + + // 메인화면에서 당일 안한 일정들 불러오기 + public List getTodoMainDaily(); + + // 월별 일정 완/미완 불러오기 (크리쳐 미션 포함) + public List getTodoCalendarCompleted(TodoRequestDTO todoRequestDTO); // 일정 추가하기 - public Todo insertTodo(Todo todo); + public TodoResponseDTO insertTodo(TodoCreateRequestDTO todoCreateRequestDTO); //일정 삭제하기 public void deleteTodo(int todoId); //일정 수정하기 - public Todo updateTodoContent(int todoId, String content); + public TodoResponseDTO updateTodo(TodoModifyRequestDTO todoModifyRequestDTO); //일정 완료하기 - public Todo completeTodo(int todo); + public TodoResponseDTO completeTodo(int todoId); - //모든 일정 완료하기 + //모든 일정 끝내기 (크리쳐 미션 포함) + public boolean AllcompleteTodo(TodoRequestDTO todoRequestDTO); // 그림일기 생성 diff --git a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoServiceImpl.java b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoServiceImpl.java index afaf9d2..4d9e896 100644 --- a/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoServiceImpl.java +++ b/Backend-feedme/feedme/src/main/java/com/todoslave/feedme/service/TodoServiceImpl.java @@ -1,10 +1,23 @@ package com.todoslave.feedme.service; +import com.todoslave.feedme.DTO.TodoCalendarResponseDTO; +import com.todoslave.feedme.DTO.TodoCreateRequestDTO; +import com.todoslave.feedme.DTO.TodoDailyRequestDTO; +import com.todoslave.feedme.DTO.TodoResponseDTO; +import com.todoslave.feedme.DTO.TodoMainResponseDTO; +import com.todoslave.feedme.DTO.TodoModifyRequestDTO; +import com.todoslave.feedme.DTO.TodoRequestDTO; +import com.todoslave.feedme.domain.entity.task.CreatureTodo; +import com.todoslave.feedme.domain.entity.task.DayOff; import com.todoslave.feedme.domain.entity.task.Todo; +import com.todoslave.feedme.login.util.SecurityUtil; +import com.todoslave.feedme.repository.CreatureTodoReposito; +import com.todoslave.feedme.repository.TodoCategoryRepository; import com.todoslave.feedme.repository.TodoRepository; -import jakarta.persistence.EntityNotFoundException; import jakarta.transaction.Transactional; -import java.util.Date; +import java.time.LocalDate; +import java.time.YearMonth; +import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -14,36 +27,233 @@ public class TodoServiceImpl implements TodoService { private final TodoRepository todoRepository; + private final TodoCategoryRepository todoCategoryRepository; + private final CreatureTodoReposito creatureTodoReposito; + private final DayOffService dayOffService; + private final CreatureService creatureService; + // 할일 목록에서 일정(일) 불러오기 @Override - public List getTodoDaily(int memberId, Date createdAt) { - return todoRepository.findAllByMemberIdAndCreatedAt(memberId, createdAt); + public List getTodoListDaily(TodoDailyRequestDTO todoDailyRequestDTO) { + + LocalDate date = todoDailyRequestDTO.getDate(); + + if(todoDailyRequestDTO.getNext()<0){ + date.minusDays(1); + }else{ + date.plusDays(1); + } + + int memberId = SecurityUtil.getCurrentUserId(); + + List query = todoRepository.findAllByMemberIdAndCreatedAt(memberId, date); + List todos = new ArrayList<>(); + + for(Todo todo : query){ + + TodoResponseDTO responseDto = new TodoResponseDTO(); + responseDto.setId(todo.getId()); + responseDto.setCategoryId(todo.getTodoCategory().getId()); + responseDto.setCategoryName(todo.getTodoCategory().getName()); + responseDto.setContent(todo.getContent()); + responseDto.setCreatedAt(todo.getCreatedAt()); + responseDto.setIsCompleted(todo.getIsCompleted()); + + todos.add(responseDto); + + } + + return todos; + } + + // 메인 달력에서 일정 불러오기 + @Override + public List getTodoCalendarDaily(TodoRequestDTO todoRequestDTO) { + + LocalDate date = todoRequestDTO.getDate(); + + int memberId = SecurityUtil.getCurrentUserId(); + List query = todoRepository.findAllByMemberIdAndCreatedAt(memberId, date); + List todos = new ArrayList<>(); + + for(Todo todo : query){ + + TodoResponseDTO responseDto = new TodoResponseDTO(); + responseDto.setId(todo.getId()); + responseDto.setCategoryId(todo.getTodoCategory().getId()); + responseDto.setCategoryName(todo.getTodoCategory().getName()); + responseDto.setContent(todo.getContent()); + responseDto.setCreatedAt(todo.getCreatedAt()); + responseDto.setIsCompleted(todo.getIsCompleted()); + + todos.add(responseDto); + + } + + return todos; + } + // 메인화면에서 당일 안한 일정들 불러오기 @Override - public Todo insertTodo(Todo todo) { - return todoRepository.save(todo); + public List getTodoMainDaily() { + + LocalDate date = LocalDate.now(); + + int memberId = SecurityUtil.getCurrentUserId(); + List query = todoRepository.findAllByMemberIdAndCreatedAtIsCompleted(memberId, date, 0); + + List todos = new ArrayList<>(); + + for(Todo todo : query){ + + TodoMainResponseDTO responseDto = new TodoMainResponseDTO(); + responseDto.setId(todo.getId()); + responseDto.setContent(todo.getContent()); + responseDto.setIsCompleted(todo.getIsCompleted()); + + todos.add(responseDto); + + } + + return todos; + } + // 월별 일정 완/미완 불러오기 + @Override + public List getTodoCalendarCompleted(TodoRequestDTO todoRequestDTO) { + + int year = todoRequestDTO.getDate().getYear(); + int month = todoRequestDTO.getDate().getMonthValue(); + + YearMonth yearMonth = YearMonth.of(year, month); + LocalDate firstDay = yearMonth.atDay(1); + LocalDate lastDay = yearMonth.atEndOfMonth(); + + List todoCounts = new ArrayList<>(); + + for (LocalDate date = firstDay; !date.isAfter(lastDay); date = date.plusDays(1)) { + + TodoCalendarResponseDTO todoCalendarResponseDTO = new TodoCalendarResponseDTO(); + + long inCompleted = todoRepository.countTodoByDateAndIsCompleted(date, 0)+creatureTodoReposito.countByCreatedAtAndIsCompleted(date,0); + + todoCalendarResponseDTO.setInCompleted((int)inCompleted); + + long completed = todoRepository.countTodoByDateAndIsCompleted(date, 1)+creatureTodoReposito.countByCreatedAtAndIsCompleted(date,1); + + todoCalendarResponseDTO.setCompleted((int)completed); + + todoCalendarResponseDTO.setTotal((int)(inCompleted+completed)); + todoCalendarResponseDTO.setDate(date); + + todoCounts.add(todoCalendarResponseDTO); + + } + + + return todoCounts; + } + + // 일정 추가하기 + @Override + public TodoResponseDTO insertTodo(TodoCreateRequestDTO todoCreateRequestDTO) { + + Todo todo = new Todo(); + todo.setMember(SecurityUtil.getCurrentMember()); + todo.setTodoCategory(todoCategoryRepository.findById(todoCreateRequestDTO.getCategoryId()).orElseThrow()); + todo.setContent(todoCreateRequestDTO.getContent()); + todo = todoRepository.save(todo); + + TodoResponseDTO todoResponseDTO = new TodoResponseDTO(); + todoResponseDTO.setId(todo.getId()); + todoResponseDTO.setContent(todo.getContent()); + todoResponseDTO.setCategoryId(todo.getTodoCategory().getId()); + todoResponseDTO.setCategoryName(todo.getTodoCategory().getName()); + todoResponseDTO.setCreatedAt(todo.getCreatedAt()); + todoResponseDTO.setIsCompleted(todo.getIsCompleted()); + + return todoResponseDTO; + } + + //일정 삭제하기 @Override - @Transactional public void deleteTodo(int todoId) { todoRepository.deleteById(todoId); } + //일정 수정하기 @Override @Transactional - public Todo updateTodoContent(int todoId, String content) { - Todo newTodo = todoRepository.findById(todoId).orElseThrow(() -> new EntityNotFoundException("Todo not found")); - newTodo.setContent(content); - return newTodo; + public TodoResponseDTO updateTodo(TodoModifyRequestDTO todoModifyRequestDTO) { + Todo todo = todoRepository.findById(todoModifyRequestDTO.getId()).orElseThrow(); + todo.setContent(todoModifyRequestDTO.getContent()); + + TodoResponseDTO todoResponseDTO = new TodoResponseDTO(); + todoResponseDTO.setId(todo.getId()); + todoResponseDTO.setContent(todo.getContent()); + todoResponseDTO.setCategoryId(todo.getTodoCategory().getId()); + todoResponseDTO.setCategoryName(todo.getTodoCategory().getName()); + todoResponseDTO.setCreatedAt(todo.getCreatedAt()); + todoResponseDTO.setIsCompleted(todo.getIsCompleted()); + + return todoResponseDTO; } + //일정 완료하기 @Override @Transactional - public Todo completeTodo(int todoId) { - Todo newTodo = todoRepository.findById(todoId).orElseThrow(() -> new EntityNotFoundException("Todo not found")); - newTodo.setCompleted(true); - return newTodo; + public TodoResponseDTO completeTodo(int todoId) { + + Todo todo = todoRepository.findById(todoId).orElseThrow(); + todo.setIsCompleted(1); + + TodoResponseDTO todoResponseDTO = new TodoResponseDTO(); + todoResponseDTO.setId(todo.getId()); + todoResponseDTO.setContent(todo.getContent()); + todoResponseDTO.setCategoryId(todo.getTodoCategory().getId()); + todoResponseDTO.setCategoryName(todo.getTodoCategory().getName()); + todoResponseDTO.setCreatedAt(todo.getCreatedAt()); + todoResponseDTO.setIsCompleted(todo.getIsCompleted()); + + return todoResponseDTO; } + + @Override + public boolean AllcompleteTodo(TodoRequestDTO todoRequestDTO) { + LocalDate date = todoRequestDTO.getDate(); + System.out.println("일"); + + //만약에 완료를 이미 했다면 + if(!dayOffService.isActionAllowed(SecurityUtil.getCurrentUserId(),date)){ + return false; + } + + System.out.println("이"); + //완료처리 + DayOff dayOff = new DayOff(); + dayOff.setEndDay(date); + dayOff.setMember(SecurityUtil.getCurrentMember()); + dayOffService.saveDayOff(dayOff); + System.out.println("삼"); + //일정 끝내기 + List todoList = todoRepository.findByMemberIdAndCreatedAt(SecurityUtil.getCurrentUserId(),date); + //크리쳐 일정 끝내기 + List creatureTodoList = creatureTodoReposito.findByMemberIdAndCreatedAt(SecurityUtil.getCurrentUserId(),date); + + + //일기 써달라고 하기 + // AI 요청!!!!!!!!!!!!!!!!!!!!! + + + int completedTodos = (int) todoList.stream().filter(todo -> todo.getIsCompleted() == 1).count(); + int completedCreatureTodos = (int) creatureTodoList.stream().filter(creatureTodo -> creatureTodo.getIsCompleted() == 1).count(); + + //경험치 올리기 + creatureService.expUp(completedTodos+completedCreatureTodos); + + //예본 해 + return true; + } } diff --git a/Backend-feedme/feedme/src/main/resources/application.properties b/Backend-feedme/feedme/src/main/resources/application.properties index 5e2ed67..bb2ad29 100644 --- a/Backend-feedme/feedme/src/main/resources/application.properties +++ b/Backend-feedme/feedme/src/main/resources/application.properties @@ -14,9 +14,6 @@ spring.data.mongodb.database=feedme spring.data.mongodb.uri=mongodb://localhost:27017/feedme spring.jpa.hibernate.ddl-auto=update -# 'create'? ?????? ?? ? ?? DB? ???? ?? ????. -# 'update'? ?? ???? ????? ??? ?? ??? ?????. - spring.jpa.show-sql=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect spring.jpa.properties.hibernate.format_sql=true @@ -25,4 +22,6 @@ logging.level.org.hibernate.SQL=debug logging.level.org.hibernate.type=trace # Hibernate? ??? SQL? ???? ??? ?? ??? ?????. +spring.jackson.time-zone=Asia/Seoul + diff --git a/Backend-feedme/feedme/src/main/resources/application.yml b/Backend-feedme/feedme/src/main/resources/application.yml new file mode 100644 index 0000000..0d56398 --- /dev/null +++ b/Backend-feedme/feedme/src/main/resources/application.yml @@ -0,0 +1,161 @@ +spring: + application: + name: feedme + admin: + enabled: false # Spring Boot Admin 비활성화 + jmx: + enabled: false # JMX 비활성화 + main: + application-class: com.todoslave.FeedmeApplication + + security: + oauth2: + client: + registration: + naver: + client-id: ${NAVER_CLIENT_ID} + client-secret: ${NAVER_CLIENT_SECRET} + client-name: Naver + redirect-uri: http://localhost:8080/login/oauth2/code/naver + authorization-grant-type: authorization_code + scope: + - name + - email + kakao: + client-id: ${KAKAO_CLIENT_ID} + client-secret: ${KAKAO_CLIENT_SECRET} + client-name: Kakao + client-authentication-method: client_secret_post + redirect-uri: http://localhost:8080/login/oauth2/code/kakao + authorization-grant-type: authorization_code + scope: + - profile_nickname + - account_email + provider: + naver: + authorization-uri: https://nid.naver.com/oauth2.0/authorize + token-uri: https://nid.naver.com/oauth2.0/token + user-info-uri: https://openapi.naver.com/v1/nid/me + user-name-attribute: response + kakao: + authorizationUri: https://kauth.kakao.com/oauth/authorize + tokenUri: https://kauth.kakao.com/oauth/token + userInfoUri: https://kapi.kakao.com/v2/user/me + userNameAttribute: id + + datasource: + url: jdbc:mysql://localhost:3306/feedme + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + + data: + mongodb: + host: localhost + port: 27017 + database: feedme + uri: mongodb://localhost:27017/feedme + + jpa: + hibernate: + ddl-auto: update + show-sql: true + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL8Dialect + format_sql: true + +logging: + level: + org: + hibernate: + SQL: debug + type: trace + +redis: + host: localhost + port: 6379 + +jwt: + issuer: todoslave@ssafy.com + secret_key: ${JWT_SECRET_KEY} + + +#spring: +# application: +# name: feedme +# main: +# application-class: com.todoslave.FeedmeApplication +# +# security: +# oauth2: +# client: +# registration: +# naver: +# client-id: QdiZgbrsCQdA7Zw1pJNN +# client-secret: iGL_mF23OA +# client-name: Naver +# redirect-uri: http://localhost:8080/login/oauth2/code/naver +# authorization-grant-type: authorization_code +# scope: +# - name +# - email +# kakao: +# client-id: e76f1a4a0c79727d85cd6b3b1969c892 +# client-secret: 69gLvDJgSBZs87Wlus2nXNKHltx0pEYg +# client-name: Kakao +# client-authentication-method: client_secret_post +# redirect-uri: http://localhost:8080/login/oauth2/code/Kakao +# authorization-grant-type: authorization_code +# scope: +# - profile_nickname +# - account_email +# provider: +# naver: +# authorization-uri: https://nid.naver.com/oauth2.0/authorize +# token-uri: https://nid.naver.com/oauth2.0/token +# user-info-uri: https://openapi.naver.com/v1/nid/me +# user-name-attribute: response +# kakao: +# authorizationUri: https://kauth.kakao.com/oauth/authorize +# tokenUri: https://kauth.kakao.com/oauth/token +# userInfoUri: https://kapi.kakao.com/v2/user/me +# userNameAttribute: id +# +# datasource: +# url: jdbc:mysql://localhost:3306/feedme +# username: ssafy +# password: ssafy +# driver-class-name: com.mysql.cj.jdbc.Driver +# +# data: +# mongodb: +# host: localhost +# port: 27017 +# database: feedme +# uri: mongodb://localhost:27017/feedme +# +# jpa: +# hibernate: +# ddl-auto: update +# show-sql: true +# properties: +# hibernate: +# dialect: org.hibernate.dialect.MySQL8Dialect +# format_sql: true +# +#logging: +# level: +# org: +# hibernate: +# SQL: debug +# type: trace +# +#redis: +# host: localhost +# port: 6379 +# +#jwt: +# issuer: todoslave@ssafy.com +# secret_key: SMidolHJFillsogoodteamjangJGnamunifristloverichHSJUSTMANBON +# diff --git a/Backend-feedme/feedme/src/main/resources/templates/articleList.html b/Backend-feedme/feedme/src/main/resources/templates/articleList.html new file mode 100644 index 0000000..cd396b6 --- /dev/null +++ b/Backend-feedme/feedme/src/main/resources/templates/articleList.html @@ -0,0 +1,35 @@ + + + + + 블로그 글 목록 + + + +
+

My Blog

+

블로그에 오신 것을 환영합니다.

+
+ +
+ +
+
+
+
+
+
+

+ 보러가기 +
+
+
+
+ + +
+ + + diff --git a/Backend-feedme/feedme/src/main/resources/templates/index.html b/Backend-feedme/feedme/src/main/resources/templates/index.html new file mode 100644 index 0000000..536b98f --- /dev/null +++ b/Backend-feedme/feedme/src/main/resources/templates/index.html @@ -0,0 +1,13 @@ + + + + + Title + + +

Hello Spring Boot

+Google Login +Naver Login +Kakao Login + + \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/resources/templates/login.html b/Backend-feedme/feedme/src/main/resources/templates/login.html new file mode 100644 index 0000000..b10cad9 --- /dev/null +++ b/Backend-feedme/feedme/src/main/resources/templates/login.html @@ -0,0 +1,13 @@ + + + + + Title + + +

Hello Spring Boot

+Google Login +Naver Login +Kakao Login + + \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/resources/templates/loginSuccess.html b/Backend-feedme/feedme/src/main/resources/templates/loginSuccess.html new file mode 100644 index 0000000..588fcef --- /dev/null +++ b/Backend-feedme/feedme/src/main/resources/templates/loginSuccess.html @@ -0,0 +1,15 @@ + + + + + Login Success + + +

Login Success

+

Access Token:

+ + + diff --git a/Backend-feedme/feedme/src/main/resources/templates/signup.html b/Backend-feedme/feedme/src/main/resources/templates/signup.html new file mode 100644 index 0000000..98e4e74 --- /dev/null +++ b/Backend-feedme/feedme/src/main/resources/templates/signup.html @@ -0,0 +1,17 @@ + + + + + Signup + + +

Signup Page

+

Email:

+

Provider:

+ + + diff --git a/Backend-feedme/feedme/src/main/resources/templates/test.html b/Backend-feedme/feedme/src/main/resources/templates/test.html new file mode 100644 index 0000000..75e40f1 --- /dev/null +++ b/Backend-feedme/feedme/src/main/resources/templates/test.html @@ -0,0 +1,11 @@ + + + + + Title + + +

요기요!!

+ + + \ No newline at end of file diff --git a/Backend-feedme/feedme/src/main/resources/templates/testsite.html b/Backend-feedme/feedme/src/main/resources/templates/testsite.html new file mode 100644 index 0000000..fa26db2 --- /dev/null +++ b/Backend-feedme/feedme/src/main/resources/templates/testsite.html @@ -0,0 +1,10 @@ + + + + + Title + + +

요기요!!

+ + \ No newline at end of file diff --git a/Front-feedme/.gitignore b/Front-feedme/.gitignore index 4d29575..532eddc 100644 --- a/Front-feedme/.gitignore +++ b/Front-feedme/.gitignore @@ -21,3 +21,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +.env \ No newline at end of file diff --git a/Front-feedme/Dockerfile b/Front-feedme/Dockerfile index 43a7c93..e83c8cd 100644 --- a/Front-feedme/Dockerfile +++ b/Front-feedme/Dockerfile @@ -8,6 +8,7 @@ WORKDIR /app COPY package.json package-lock.json ./ RUN npm install --silent +# Copy the rest of your app's source code from your host to your image filesystem. COPY . . # Build the application @@ -16,12 +17,22 @@ RUN npm run build # Stage 2: Serve the application using Nginx FROM nginx:stable-alpine as production-stage +# Create a directory for Jenkins logs +RUN mkdir -p /var/log/nginx/jenkins + +# Set permissions if necessary (This step may not be necessary depending on your usage) +RUN chmod -R 755 /var/log/nginx + # Remove the default Nginx configuration COPY ./default.conf /etc/nginx/conf.d # Copy the built application from the build stage to the Nginx HTML directory COPY --from=build /app/build /usr/share/nginx/html +# Ensure logging statements go to the standard output (for easier debugging and logging) +RUN ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log + # Expose port 80 and 443 (HTTPS) EXPOSE 80 diff --git a/Front-feedme/README.md b/Front-feedme/README.md index 201473e..58beeac 100644 --- a/Front-feedme/README.md +++ b/Front-feedme/README.md @@ -1,17 +1,3 @@ -# frontend - -### 화면 구조 잡기 --src - - api : http 요청들을 관리하여 구분한 폴더 - - assets : 프로젝트의 자산 관리(폰트, 이미지, 동영상 등을 저장하여 프로젝트에서 사용할 수 있도록 저장) - - font - - images - - components : 공통적으로 사용하는 component들을 관리, 여러가지 페이지에 렌더링 될 수 있는 컴포넌트들을 재사용 가능하게 정리 - - hooks : custom hook들을 담아 놓은 폴더 - - pages : 페이지 단위의 컴포넌트를 폴더로 구성, 어떤 특정 화면에 위치하였을 때 렌더링 될 코드, 특정 페이지에서만 사용되는 컴포넌트는 components 폴더로 분리하기 보다 해당 페이지 폴더의 하위 폴더에서 관리하는 것이 좋을 듯 - - store : redux를 정의 해놓은 폴더 - - # Getting Started with Create React App This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). diff --git a/Front-feedme/default.conf b/Front-feedme/default.conf index 875a5e1..ca2229e 100644 --- a/Front-feedme/default.conf +++ b/Front-feedme/default.conf @@ -1,26 +1,66 @@ +# WebSocket 지원을 위한 설정 +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +# HTTP 요청을 HTTPS로 리디렉션하는 서버 블록 server { listen 80; server_name i11b104.p.ssafy.io; + location / { return 301 https://$host$request_uri; } } +# HTTPS 설정을 위한 서버 블록. server { listen 443 ssl; server_name i11b104.p.ssafy.io; + + # 로그 설정 access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; + # SSL 인증서 설정 ssl_certificate /etc/letsencrypt/live/i11b104.p.ssafy.io/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/i11b104.p.ssafy.io/privkey.pem; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv3; - ssl_ciphers ALL; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + # 잘못된 헤더 무시 설정 + ignore_invalid_headers off; + + # Jenkins 정적 파일 처리 + location ~ "^/static/[0-9a-fA-F]{8}/(.*)$" { + # 정적 파일 요청을 루트로 리다이렉트 + rewrite "^/static/[0-9a-fA-F]{8}/(.*)" /$1 last; + } + + # Jenkins 사용자 콘텐츠 처리 + location /userContent { + root /var/lib/jenkins/; # Jenkins 사용자 콘텐츠 디렉토리 + if (!-f $request_filename){ + rewrite (.*) /$1 last; + break; + } + sendfile on; + } + + # 기본 웹 페이지 제공을 위한 location 블록 location / { + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'Origin, Accept, Content-Type, Authorization'; + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } root /usr/share/nginx/html; - index index.html index.htm - proxy_redirect off; + index index.html index.htm; charset utf-8; try_files $uri $uri/ /index.html; @@ -31,7 +71,71 @@ server { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; proxy_set_header X-Nginx-Proxy true; } + # API 요청을 백엔드 서버로 프록시하는 location 블록 + location /api/ { + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'Origin, Accept, Content-Type, Authorization'; + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + + proxy_pass http://back-server:8085; # Spring 백엔드 서버를 처리 + } + + # Jenkins에 대한 요청을 프록시하는 location 블록 + location /jenkins/ { + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'Origin, Accept, Content-Type, Authorization'; + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + sendfile off; + proxy_pass http://jenkins:8080/jenkins/; # Jenkins 컨테이너로 프록시 + proxy_redirect http://jenkins:8080/jenkins/ https://$host/jenkins/; + proxy_http_version 1.1; + + # Jenkins 웹소켓 에이전트 지원 + proxy_set_header Connection $connection_upgrade; + proxy_set_header Upgrade $http_upgrade; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_max_temp_file_size 0; + + # 업로드 크기 제한 + client_max_body_size 10m; + client_body_buffer_size 128k; + + # 프록시 타임아웃 설정 + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + proxy_request_buffering off; # HTTP CLI 명령어 지원 + } } diff --git a/Front-feedme/docker-compose.yml b/Front-feedme/docker-compose.yml deleted file mode 100644 index de96fc6..0000000 --- a/Front-feedme/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: '3' -services: - certbot: - image: certbot/certbot - volumes: - - /home/ubuntu/project/certbot/etc:/etc/letsencrypt - - /home/ubuntu/project/certbot/lib:/var/lib/letsencrypt - - /home/ubuntu/project/certbot/log:/var/log/letsencrypt - entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h; done;'" - - react-app: - build: - context: . - dockerfile: Dockerfile - image: doki2580/feedme-front:latest - ports: - - "8081:80" - - "443:443" - volumes: - - ./certs/fullchain.pem:/etc/ssl/certs/fullchain.pem - - ./certs/privkey.pem:/etc/ssl/private/privkey.pem - depends_on: - - certbot - restart: always diff --git a/Front-feedme/package-lock.json b/Front-feedme/package-lock.json index 06fd846..109e43f 100644 --- a/Front-feedme/package-lock.json +++ b/Front-feedme/package-lock.json @@ -10,27 +10,41 @@ "dependencies": { "@emotion/react": "^11.13.0", "@emotion/style": "^0.8.0", + "@emotion/styled": "^11.13.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", - "@mui/material": "^5.16.4", - "@reduxjs/toolkit": "^2.2.6", + "@mui/icons-material": "^5.16.6", + "@mui/material": "^5.16.6", + "@reduxjs/toolkit": "^2.2.7", + "@stomp/stompjs": "^7.0.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "axios": "^1.7.2", + "antd": "^5.20.0", + "axios": "^1.7.3", + "date-fns": "^3.6.0", + "dayjs": "^1.11.12", + "event-source-polyfill": "^1.0.31", "framer-motion": "^11.3.12", + "http-proxy-middleware": "^3.0.0", "qs": "^6.12.3", "quill": "^2.0.2", "react": "^18.3.1", + "react-calendar": "^5.0.0", "react-dom": "^18.3.1", "react-helmet-async": "^2.0.5", + "react-icons": "^5.2.1", + "react-modal": "^3.16.1", "react-redux": "^9.1.2", "react-router-dom": "^6.25.1", "react-scripts": "5.0.1", "react-social-login-buttons": "^4.1.0", "redux-saga": "^1.3.0", "sanitize-html": "^2.13.0", + "sockjs-client": "^1.6.1", + "stompjs": "^2.3.3", + "styled-components": "^6.1.12", "web-vitals": "^2.1.4" } }, @@ -65,6 +79,106 @@ "node": ">=6.0.0" } }, + "node_modules/@ant-design/colors": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.1.0.tgz", + "integrity": "sha512-MMoDGWn1y9LdQJQSHiCC20x3uZ3CwQnv9QMz6pCmJOrqdgM9YxsoVVY0wtrdXbmfSgnV0KNk6zi09NAhMR2jvg==", + "dependencies": { + "@ctrl/tinycolor": "^3.6.1" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.21.0.tgz", + "integrity": "sha512-gIilraPl+9EoKdYxnupxjHB/Q6IHNRjEXszKbDxZdsgv4sAZ9pjkCq8yanDWNvyfjp4leir2OVAJm0vxwKK8YA==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "classnames": "^2.3.1", + "csstype": "^3.1.3", + "rc-util": "^5.35.0", + "stylis": "^4.0.13" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.0.3.tgz", + "integrity": "sha512-BrztZZKuoYcJK8uEH40ylBemf/Mu/QPiDos56g2bv6eUoniQkgQHOCOvA3+pncoFO1TaS8xcUCIqGzDA0I+ZVQ==", + "dependencies": { + "@ant-design/cssinjs": "^1.21.0", + "@babel/runtime": "^7.23.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/cssinjs/node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "node_modules/@ant-design/cssinjs/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "node_modules/@ant-design/fast-color": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.4.tgz", + "integrity": "sha512-z/9LMF7SQcPpTdMkcWRmTwvlwCDQoQHexL4GFz0UDlTrzUG/Y5FjnkjuR4+Y1Kis0Mf34tTlZmSx07QOm4eWxQ==", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.4.0.tgz", + "integrity": "sha512-QZbWC5xQYexCI5q4/fehSEkchJr5UGtvAJweT743qKUQQGs9IH2DehNLP49DJ3Ii9m9CijD2HN6fNy3WKhIFdA==", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==" + }, + "node_modules/@ant-design/react-slick": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz", + "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==", + "dependencies": { + "@babel/runtime": "^7.10.4", + "classnames": "^2.2.5", + "json2mq": "^0.2.0", + "resize-observer-polyfill": "^1.5.1", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": ">=16.9.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -2474,6 +2588,14 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "engines": { + "node": ">=10" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", @@ -2591,6 +2713,19 @@ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", "license": "MIT" }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/is-prop-valid/node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, "node_modules/@emotion/memoize": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", @@ -2601,7 +2736,6 @@ "version": "11.13.0", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.0.tgz", "integrity": "sha512-WkL+bw1REC2VNV1goQyfxjx1GYJkcc23CRQkXX+vZNLINyfI7o+uUn/rTGPt/xJ3bJHd5GcljgnxHf4wRw5VWQ==", - "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.12.0", @@ -2720,6 +2854,58 @@ "integrity": "sha512-HZlSY1AxnK/Vi/zZx7Lacliqo74G/5zn0HqgCtC0ucBkO3BW2S1XCErlZxVMVoYi6r3qyn8wE0nQltbu/bx1+A==", "license": "MIT" }, + "node_modules/@emotion/styled": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz", + "integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/styled/node_modules/@emotion/is-prop-valid": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.0.tgz", + "integrity": "sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/styled/node_modules/@emotion/serialize": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.0.tgz", + "integrity": "sha512-jACuBa9SlYajnpIVXB+XOXnfJHyckDfe6fOpORIM6yhBDlqGuExvDdZYHDQGoDf3bZXGv7tNr+LpLjJqiEQ6EA==", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.9.0", + "@emotion/utils": "^1.4.0", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/styled/node_modules/@emotion/unitless": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.9.0.tgz", + "integrity": "sha512-TP6GgNZtmtFaFcsOgExdnfxLLpRDla4Q66tnenA9CktvVSdNKDvMVuUah4QvWPIpNjrWsGg3qeGo9a43QooGZQ==" + }, + "node_modules/@emotion/styled/node_modules/@emotion/utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz", + "integrity": "sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==" + }, "node_modules/@emotion/stylis": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.7.1.tgz", @@ -2867,7 +3053,6 @@ "version": "6.6.0", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==", - "license": "MIT", "engines": { "node": ">=6" } @@ -2876,7 +3061,6 @@ "version": "6.6.0", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz", "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==", - "license": "MIT", "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" }, @@ -2900,7 +3084,6 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", - "license": "MIT", "dependencies": { "prop-types": "^15.8.1" }, @@ -3789,26 +3972,49 @@ "license": "MIT" }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.4.tgz", - "integrity": "sha512-rNdHXhclwjEZnK+//3SR43YRx0VtjdHnUFhMSGYmAMJve+KiwEja/41EYh8V3pZKqF2geKyfcFUenTfDTYUR4w==", - "license": "MIT", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.6.tgz", + "integrity": "sha512-kytg6LheUG42V8H/o/Ptz3olSO5kUXW9zF0ox18VnblX6bO2yif1FPItgc3ey1t5ansb1+gbe7SatntqusQupg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.6.tgz", + "integrity": "sha512-ceNGjoXheH9wbIFa1JHmSc9QVjJUvh18KvHrR4/FkJCSi9HXJ+9ee1kUhCOEFfuxNF8UB6WWVrIUOUgRd70t0A==", + "dependencies": { + "@babel/runtime": "^7.23.9" + }, + "engines": { + "node": ">=12.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/@mui/material": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.4.tgz", - "integrity": "sha512-dBnh3/zRYgEVIS3OE4oTbujse3gifA0qLMmuUk13ywsDCbngJsdgwW5LuYeiT5pfA8PGPGSqM7mxNytYXgiMCw==", - "license": "MIT", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.6.tgz", + "integrity": "sha512-0LUIKBOIjiFfzzFNxXZBRAyr9UQfmTAFzbt6ziOU2FDXhorNN2o3N9/32mNJbCA8zJo2FqFU6d3dtoqUDyIEfA==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/core-downloads-tracker": "^5.16.4", - "@mui/system": "^5.16.4", + "@mui/core-downloads-tracker": "^5.16.6", + "@mui/system": "^5.16.6", "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.4", + "@mui/utils": "^5.16.6", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.10", "clsx": "^2.1.0", @@ -3850,13 +4056,12 @@ "license": "MIT" }, "node_modules/@mui/private-theming": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.4.tgz", - "integrity": "sha512-ZsAm8cq31SJ37SVWLRlu02v9SRthxnfQofaiv14L5Bht51B0dz6yQEoVU/V8UduZDCCIrWkBHuReVfKhE/UuXA==", - "license": "MIT", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.6.tgz", + "integrity": "sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.16.4", + "@mui/utils": "^5.16.6", "prop-types": "^15.8.1" }, "engines": { @@ -3877,10 +4082,9 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.4.tgz", - "integrity": "sha512-0+mnkf+UiAmTVB8PZFqOhqf729Yh0Cxq29/5cA3VAyDVTRIUUQ8FXQhiAhUIbijFmM72rY80ahFPXIm4WDbzcA==", - "license": "MIT", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.6.tgz", + "integrity": "sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==", "dependencies": { "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", @@ -3909,10 +4113,9 @@ } }, "node_modules/@mui/styled-engine/node_modules/@emotion/cache": { - "version": "11.13.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.0.tgz", - "integrity": "sha512-hPV345J/tH0Cwk2wnU/3PBzORQ9HeX+kQSbwI+jslzpRCHE6fSGTohswksA/Ensr8znPzwfzKZCmAM9Lmlhp7g==", - "license": "MIT", + "version": "11.13.1", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", + "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", @@ -3924,26 +4127,23 @@ "node_modules/@mui/styled-engine/node_modules/@emotion/sheet": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", - "license": "MIT" + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" }, "node_modules/@mui/styled-engine/node_modules/@emotion/utils": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==", - "license": "MIT" + "integrity": "sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==" }, "node_modules/@mui/system": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.4.tgz", - "integrity": "sha512-ET1Ujl2/8hbsD611/mqUuNArMCGv/fIWO/f8B3ZqF5iyPHM2aS74vhTNyjytncc4i6dYwGxNk+tLa7GwjNS0/w==", - "license": "MIT", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.6.tgz", + "integrity": "sha512-5xgyJjBIMPw8HIaZpfbGAaFYPwImQn7Nyh+wwKWhvkoIeDosQ1ZMVrbTclefi7G8hNmqhip04duYwYpbBFnBgw==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.16.4", - "@mui/styled-engine": "^5.16.4", + "@mui/private-theming": "^5.16.6", + "@mui/styled-engine": "^5.16.6", "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.4", + "@mui/utils": "^5.16.6", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -3977,7 +4177,6 @@ "version": "7.2.15", "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", - "license": "MIT", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0" }, @@ -3988,12 +4187,12 @@ } }, "node_modules/@mui/utils": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.4.tgz", - "integrity": "sha512-nlppYwq10TBIFqp7qxY0SvbACOXeOjeVL3pOcDsK0FT8XjrEXh9/+lkg8AEIzD16z7YfiJDQjaJG2OLkE7BxNg==", - "license": "MIT", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", + "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", "dependencies": { "@babel/runtime": "^7.23.9", + "@mui/types": "^7.2.15", "@types/prop-types": "^15.7.12", "clsx": "^2.1.1", "prop-types": "^15.8.1", @@ -4019,8 +4218,7 @@ "node_modules/@mui/utils/node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", @@ -4156,6 +4354,146 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rc-component/async-validator": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz", + "integrity": "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.0.tgz", + "integrity": "sha512-52z3XqUwUr0+Br3B8RjN2GfuR1Pk3MZPAVd34WptWFEOyTz7OQmmn8nqgXUBOYwZem8jXp6G3iv+6Dm1+1epJA==", + "dependencies": { + "@ant-design/fast-color": "^2.0.1", + "@babel/runtime": "^7.23.6", + "classnames": "^2.2.6", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz", + "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz", + "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz", + "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz", + "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.0.0.tgz", + "integrity": "sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==", + "dependencies": { + "@babel/runtime": "^7.24.7", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.0.tgz", + "integrity": "sha512-h6hyILDwL+In9GAgRobwRWihLqqsD7Uft3fZGrJ7L4EiyCoxbnNYwzPXDfz7vNDhWeVyvAWQJj9fJCzpI4+b4g==", + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/portal": "^1.0.0-9", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/trigger": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.0.tgz", + "integrity": "sha512-QarBCji02YE9aRFhZgRZmOpXBj0IZutRippsVBv85sxvG4FGk/vRxwAlkn3MS9zK5mwbETd86mAVg2tKqTkdJA==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@rc-component/portal": "^1.1.0", + "classnames": "^2.3.2", + "rc-motion": "^2.0.0", + "rc-resize-observer": "^1.3.1", + "rc-util": "^5.38.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/@redux-saga/core": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.3.0.tgz", @@ -4213,10 +4551,9 @@ "license": "MIT" }, "node_modules/@reduxjs/toolkit": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.6.tgz", - "integrity": "sha512-kH0r495c5z1t0g796eDQAkYbEQ3a1OLYN9o8jQQVZyKyw367pfRGS+qZLkHYvFHiUUdafpoSlQ2QYObIApjPWA==", - "license": "MIT", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.7.tgz", + "integrity": "sha512-faI3cZbSdFb8yv9dhDTmGwclW0vk0z5o1cia+kf7gCbaCwHI5e+7tP57mJUv22pNcNbeA62GSrPpfrUfdXcQ6g==", "dependencies": { "immer": "^10.0.3", "redux": "^5.0.1", @@ -4364,6 +4701,12 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@stomp/stompjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.0.0.tgz", + "integrity": "sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw==", + "license": "Apache-2.0" + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -5545,6 +5888,11 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "license": "MIT" }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", @@ -5971,6 +6319,14 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@wojtekmaj/date-utils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.5.1.tgz", + "integrity": "sha512-+i7+JmNiE/3c9FKxzWFi2IjRJ+KzZl1QPu6QNrsgaa2MuBgXvUy4gA1TVzf/JMdIIloB76xSKikTWuyYAIVLww==", + "funding": { + "url": "https://github.com/wojtekmaj/date-utils?sponsor=1" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -6222,6 +6578,70 @@ "node": ">=4" } }, + "node_modules/antd": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.20.0.tgz", + "integrity": "sha512-wWCFzbry3hov7k8gqhPR+FzD6EkWlhBbGD9mYOSIDoYRGMRqueTh2+2jfU1voHucmwcxDwzU7iwZDU2+PCXZdA==", + "dependencies": { + "@ant-design/colors": "^7.1.0", + "@ant-design/cssinjs": "^1.21.0", + "@ant-design/cssinjs-utils": "^1.0.3", + "@ant-design/icons": "^5.4.0", + "@ant-design/react-slick": "~1.1.2", + "@babel/runtime": "^7.24.8", + "@ctrl/tinycolor": "^3.6.1", + "@rc-component/color-picker": "~2.0.0", + "@rc-component/mutate-observer": "^1.1.0", + "@rc-component/qrcode": "~1.0.0", + "@rc-component/tour": "~1.15.0", + "@rc-component/trigger": "^2.2.0", + "classnames": "^2.5.1", + "copy-to-clipboard": "^3.3.3", + "dayjs": "^1.11.11", + "rc-cascader": "~3.27.0", + "rc-checkbox": "~3.3.0", + "rc-collapse": "~3.7.3", + "rc-dialog": "~9.5.2", + "rc-drawer": "~7.2.0", + "rc-dropdown": "~4.2.0", + "rc-field-form": "~2.2.1", + "rc-image": "~7.9.0", + "rc-input": "~1.6.2", + "rc-input-number": "~9.2.0", + "rc-mentions": "~2.15.0", + "rc-menu": "~9.14.1", + "rc-motion": "^2.9.2", + "rc-notification": "~5.6.0", + "rc-pagination": "~4.2.0", + "rc-picker": "~4.6.11", + "rc-progress": "~4.0.0", + "rc-rate": "~2.13.0", + "rc-resize-observer": "^1.4.0", + "rc-segmented": "~2.3.0", + "rc-select": "~14.15.1", + "rc-slider": "~11.1.3", + "rc-steps": "~6.0.1", + "rc-switch": "~4.1.0", + "rc-table": "~7.45.7", + "rc-tabs": "~15.1.1", + "rc-textarea": "~1.8.1", + "rc-tooltip": "~6.2.0", + "rc-tree": "~5.8.8", + "rc-tree-select": "~5.22.1", + "rc-upload": "~4.6.0", + "rc-util": "^5.43.0", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -6307,6 +6727,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-tree-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz", + "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==" + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -6546,10 +6971,9 @@ } }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", - "license": "MIT", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", + "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -7101,6 +7525,20 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -7181,6 +7619,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -7317,6 +7763,11 @@ "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", "license": "MIT" }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -7502,6 +7953,11 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", + "integrity": "sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -7565,6 +8021,14 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "license": "MIT" }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core-js": { "version": "3.37.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", @@ -7663,6 +8127,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -7812,6 +8284,16 @@ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "license": "MIT" }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -8026,6 +8508,20 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "license": "ISC", + "optional": true, + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -8097,11 +8593,25 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "license": "MIT", + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/dayjs": { + "version": "1.11.12", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz", + "integrity": "sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==" + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -8802,6 +9312,49 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "license": "ISC", + "optional": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", + "optional": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", + "optional": true, + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -9495,6 +10048,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", + "optional": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -9582,6 +10151,22 @@ "node": ">= 0.6" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", + "optional": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/event-source-polyfill": { + "version": "1.0.31", + "resolved": "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz", + "integrity": "sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA==" + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -9597,6 +10182,15 @@ "node": ">=0.8.x" } }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -9620,6 +10214,11 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -9715,6 +10314,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", + "optional": true, + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10444,6 +11053,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-user-locale": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-2.3.2.tgz", + "integrity": "sha512-O2GWvQkhnbDoWFUJfaBlDIKUEdND8ATpBXD6KXcbhxlfktyD/d8w6mkzM/IlQEqGZAMz/PW6j6Hv53BiigKLUQ==", + "dependencies": { + "mem": "^8.0.0" + }, + "funding": { + "url": "https://github.com/wojtekmaj/get-user-locale?sponsor=1" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -10942,27 +11562,20 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.0.tgz", + "integrity": "sha512-36AV1fIaI2cWRzHo+rbcxhe3M3jUDCNzc4D5zRl57sEWRAxdXYtw7FSQKYY6PDKssiAKjLYypbssHk+xs/kMXw==", "license": "MIT", "dependencies": { - "@types/http-proxy": "^1.17.8", + "@types/http-proxy": "^1.17.10", + "debug": "^4.3.4", "http-proxy": "^1.18.1", "is-glob": "^4.0.1", "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" + "micromatch": "^4.0.5" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/https-proxy-agent": { @@ -14061,6 +14674,14 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "license": "MIT" }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "dependencies": { + "string-convert": "^0.2.0" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -14403,6 +15024,17 @@ "tmpl": "1.0.5" } }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -14418,6 +15050,29 @@ "node": ">= 0.6" } }, + "node_modules/mem": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", + "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==", + "dependencies": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/mem?sponsor=1" + } + }, + "node_modules/mem/node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/memfs": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", @@ -14667,6 +15322,13 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "license": "MIT" }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC", + "optional": true + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -14686,6 +15348,18 @@ "node": ">= 6.13.0" } }, + "node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -15000,6 +15674,14 @@ "node": ">= 0.8.0" } }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -16901,145 +17583,746 @@ "node": ">=0.10.0" } }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", + "node_modules/rc-cascader": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.27.0.tgz", + "integrity": "sha512-z5uq8VvQadFUBiuZJ7YF5UAUGNkZtdEtcEYiIA94N/Kc2MIKr6lEbN5HyVddvYSgwWlKqnL6pH5bFXFuIK3MNg==", "dependencies": { - "loose-envify": "^1.1.0" + "@babel/runtime": "^7.12.5", + "array-tree-filter": "^2.1.0", + "classnames": "^2.3.1", + "rc-select": "~14.15.0", + "rc-tree": "~5.8.1", + "rc-util": "^5.37.0" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/react-app-polyfill": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", - "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", - "license": "MIT", + "node_modules/rc-checkbox": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.3.0.tgz", + "integrity": "sha512-Ih3ZaAcoAiFKJjifzwsGiT/f/quIkxJoklW4yKGho14Olulwn8gN7hOBve0/WGDg5o/l/5mL0w7ff7/YGvefVw==", "dependencies": { - "core-js": "^3.19.2", - "object-assign": "^4.1.1", - "promise": "^8.1.0", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.9", - "whatwg-fetch": "^3.6.2" + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.25.2" }, - "engines": { - "node": ">=14" + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/react-app-polyfill/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT" + "node_modules/rc-collapse": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.7.3.tgz", + "integrity": "sha512-60FJcdTRn0X5sELF18TANwtVi7FtModq649H11mYF1jh83DniMoM4MqY627sEKRCTm4+WXfGDcB7hY5oW6xhyw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.3.4", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } }, - "node_modules/react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "license": "MIT", + "node_modules/rc-dialog": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.5.2.tgz", + "integrity": "sha512-qVUjc8JukG+j/pNaHVSRa2GO2/KbV2thm7yO4hepQ902eGdYK913sGkwg/fh9yhKYV1ql3BKIN2xnud3rEXAPw==", "dependencies": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "@babel/runtime": "^7.10.1", + "@rc-component/portal": "^1.0.0-8", + "classnames": "^2.2.6", + "rc-motion": "^2.3.0", + "rc-util": "^5.21.0" }, - "engines": { - "node": ">=14" + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/react-dev-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", + "node_modules/rc-drawer": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.2.0.tgz", + "integrity": "sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg==", "dependencies": { - "color-convert": "^2.0.1" + "@babel/runtime": "^7.23.9", + "@rc-component/portal": "^1.1.1", + "classnames": "^2.2.6", + "rc-motion": "^2.6.1", + "rc-util": "^5.38.1" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dropdown": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.0.tgz", + "integrity": "sha512-odM8Ove+gSh0zU27DUj5cG1gNKg7mLWBYzB5E4nNLrLwBmYEgYP43vHKDGOVZcJSVElQBI0+jTQgjnq0NfLjng==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-util": "^5.17.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" } }, - "node_modules/react-dev-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", + "node_modules/rc-field-form": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.2.1.tgz", + "integrity": "sha512-uoNqDoR7A4tn4QTSqoWPAzrR7ZwOK5I+vuZ/qdcHtbKx+ZjEsTg7QXm2wk/jalDiSksAQmATxL0T5LJkRREdIA==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@babel/runtime": "^7.18.0", + "@rc-component/async-validator": "^5.0.3", + "rc-util": "^5.32.2" }, "engines": { - "node": ">=10" + "node": ">=8.x" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/react-dev-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", + "node_modules/rc-image": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.9.0.tgz", + "integrity": "sha512-l4zqO5E0quuLMCtdKfBgj4Suv8tIS011F5k1zBBlK25iMjjiNHxA0VeTzGFtUZERSA45gvpXDg8/P6qNLjR25g==", "dependencies": { - "color-name": "~1.1.4" + "@babel/runtime": "^7.11.2", + "@rc-component/portal": "^1.0.2", + "classnames": "^2.2.6", + "rc-dialog": "~9.5.2", + "rc-motion": "^2.6.2", + "rc-util": "^5.34.1" }, - "engines": { - "node": ">=7.0.0" + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/react-dev-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/react-dev-utils/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" + "node_modules/rc-input": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.6.2.tgz", + "integrity": "sha512-nJqsiIv8K88w8pvbUR5savKqBokdSR0zVGPntLApeOKFp8dp6s92l1CzD60yVActpCZAJwlCfRX5rno+QVYV7g==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.18.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" } }, - "node_modules/react-dev-utils/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", + "node_modules/rc-input-number": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.2.0.tgz", + "integrity": "sha512-5XZFhBCV5f9UQ62AZ2hFbEY8iZT/dm23Q1kAg0H8EvOgD3UDbYYJAayoVIkM3lQaCqYAW5gV0yV3vjw1XtzWHg==", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "@babel/runtime": "^7.10.1", + "@rc-component/mini-decimal": "^1.0.1", + "classnames": "^2.2.5", + "rc-input": "~1.6.0", + "rc-util": "^5.40.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-mentions": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.15.0.tgz", + "integrity": "sha512-f5v5i7VdqvBDXbphoqcQWmXDif2Msd2arritVoWybrVDuHE6nQ7XCYsybHbV//WylooK52BFDouFvyaRDtXZEw==", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-input": "~1.6.0", + "rc-menu": "~9.14.0", + "rc-textarea": "~1.8.0", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-menu": { + "version": "9.14.1", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.14.1.tgz", + "integrity": "sha512-5wlRb3M8S4yGlWhSoEYJ7ZVRElyScdcpUHxgiLxkeig1tEdyKrnED3B2fhpN0Rrpdp9jyhnmZR/Lwq2fH5VvDQ==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.0.0", + "classnames": "2.x", + "rc-motion": "^2.4.3", + "rc-overflow": "^1.3.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-motion": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.2.tgz", + "integrity": "sha512-fUAhHKLDdkAXIDLH0GYwof3raS58dtNUmzLF2MeiR8o6n4thNpSDQhOqQzWE4WfFZDCi9VEN8n7tiB7czREcyw==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-notification": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.0.tgz", + "integrity": "sha512-TGQW5T7waOxLwgJG7fXcw8l7AQiFOjaZ7ISF5PrU526nunHRNcTMuzKihQHaF4E/h/KfOCDk3Mv8eqzbu2e28w==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.9.0", + "rc-util": "^5.20.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-overflow": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.3.2.tgz", + "integrity": "sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.37.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-pagination": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-4.2.0.tgz", + "integrity": "sha512-V6qeANJsT6tmOcZ4XiUmj8JXjRLbkusuufpuoBw2GiAn94fIixYjFLmbruD1Sbhn8fPLDnWawPp4CN37zQorvw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-picker": { + "version": "4.6.11", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.6.11.tgz", + "integrity": "sha512-PEVH5MMTUrdvTTxCmPndsXiJL7TFLSu8q0cDdZrhdcjn8en3NbuhOFacWqKTvdnfG53RPPhiBssXCUHYyc3R/Q==", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.1", + "rc-overflow": "^1.3.2", + "rc-resize-observer": "^1.4.0", + "rc-util": "^5.43.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/rc-progress": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz", + "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-util": "^5.16.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-rate": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.0.tgz", + "integrity": "sha512-oxvx1Q5k5wD30sjN5tqAyWTvJfLNNJn7Oq3IeS4HxWfAiC4BOXMITNAsw7u/fzdtO4MS8Ki8uRLOzcnEuoQiAw==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-resize-observer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz", + "integrity": "sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q==", + "dependencies": { + "@babel/runtime": "^7.20.7", + "classnames": "^2.2.1", + "rc-util": "^5.38.0", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-segmented": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.3.0.tgz", + "integrity": "sha512-I3FtM5Smua/ESXutFfb8gJ8ZPcvFR+qUgeeGFQHBOvRiRKyAk4aBE5nfqrxXx+h8/vn60DQjOt6i4RNtrbOobg==", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-motion": "^2.4.4", + "rc-util": "^5.17.0" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-select": { + "version": "14.15.1", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.15.1.tgz", + "integrity": "sha512-mGvuwW1RMm1NCSI8ZUoRoLRK51R2Nb+QJnmiAvbDRcjh2//ulCkxeV6ZRFTECPpE1t2DPfyqZMPw90SVJzQ7wQ==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.1.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-overflow": "^1.3.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-slider": { + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.5.tgz", + "integrity": "sha512-b77H5PbjMKsvkYXAYIkn50QuFX6ICQmCTibDinI9q+BHx65/TV4TeU25+oadhSRzykxs0/vBWeKBwRyySOeWlg==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-steps": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz", + "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==", + "dependencies": { + "@babel/runtime": "^7.16.7", + "classnames": "^2.2.3", + "rc-util": "^5.16.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-switch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz", + "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "classnames": "^2.2.1", + "rc-util": "^5.30.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-table": { + "version": "7.45.7", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.45.7.tgz", + "integrity": "sha512-wi9LetBL1t1csxyGkMB2p3mCiMt+NDexMlPbXHvQFmBBAsMxrgNSAPwUci2zDLUq9m8QdWc1Nh8suvrpy9mXrg==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/context": "^1.4.0", + "classnames": "^2.2.5", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.37.0", + "rc-virtual-list": "^3.14.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tabs": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.1.1.tgz", + "integrity": "sha512-Tc7bJvpEdkWIVCUL7yQrMNBJY3j44NcyWS48jF/UKMXuUlzaXK+Z/pEL5LjGcTadtPvVmNqA40yv7hmr+tCOAw==", + "dependencies": { + "@babel/runtime": "^7.11.2", + "classnames": "2.x", + "rc-dropdown": "~4.2.0", + "rc-menu": "~9.14.0", + "rc-motion": "^2.6.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.34.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-textarea": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.8.1.tgz", + "integrity": "sha512-bm36N2ZqwZAP60ZQg2OY9mPdqWC+m6UTjHc+CqEZOxb3Ia29BGHazY/s5bI8M4113CkqTzhtFUDNA078ZiOx3Q==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-input": "~1.6.0", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tooltip": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.2.0.tgz", + "integrity": "sha512-iS/3iOAvtDh9GIx1ulY7EFUXUtktFccNLsARo3NPgLf0QW9oT0w3dA9cYWlhqAKmD+uriEwdWz1kH0Qs4zk2Aw==", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tree": { + "version": "5.8.8", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.8.8.tgz", + "integrity": "sha512-S+mCMWo91m5AJqjz3PdzKilGgbFm7fFJRFiTDOcoRbD7UfMOPnerXwMworiga0O2XIo383UoWuEfeHs1WOltag==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-tree-select": { + "version": "5.22.1", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.22.1.tgz", + "integrity": "sha512-b8mAK52xEpRgS+b2PTapCt29GoIrO5cO8jB7AfHttFsIJfcnynY9FCtnYzURsKXJkGHbFY6UzSEB2I3TETtdWg==", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-select": "~14.15.0", + "rc-tree": "~5.8.1", + "rc-util": "^5.16.1" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-upload": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.6.0.tgz", + "integrity": "sha512-Zr0DT1NHw/ApxrP7UAoxOtGaVYuzarrrCVr0ld7RiEFsKX07uFhE1EpCBxwL11ruFn89GMcshOKWp+s6FLyAlA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "classnames": "^2.2.5", + "rc-util": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util": { + "version": "5.43.0", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.43.0.tgz", + "integrity": "sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/rc-virtual-list": { + "version": "3.14.5", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.14.5.tgz", + "integrity": "sha512-ZMOnkCLv2wUN8Jz7yI4XiSLa9THlYvf00LuMhb1JlsQCewuU7ydPuHw1rGVPhe9VZYl/5UqODtNd7QKJ2DMGfg==", + "dependencies": { + "@babel/runtime": "^7.20.0", + "classnames": "^2.2.6", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "license": "MIT", + "dependencies": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-app-polyfill/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, + "node_modules/react-calendar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-5.0.0.tgz", + "integrity": "sha512-bHcE5e5f+VUKLd4R19BGkcSQLpuwjKBVG0fKz74cwPW5xDfNsReHdDbfd4z3mdjuUuZzVtw4Q920mkwK5/ZOEg==", + "dependencies": { + "@wojtekmaj/date-utils": "^1.1.3", + "clsx": "^2.0.0", + "get-user-locale": "^2.2.1", + "warning": "^4.0.0" + }, + "funding": { + "url": "https://github.com/wojtekmaj/react-calendar?sponsor=1" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-dev-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/react-dev-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/react-dev-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/react-dev-utils/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { "node": ">=10" @@ -17162,17 +18445,48 @@ "react": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "license": "MIT" }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-modal": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", + "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" + } + }, "node_modules/react-redux": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", - "license": "MIT", "dependencies": { "@types/use-sync-external-store": "^0.0.3", "use-sync-external-store": "^1.0.0" @@ -17580,6 +18894,11 @@ "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", "license": "MIT" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -18111,6 +19430,14 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -18413,6 +19740,34 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs-client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.1.tgz", + "integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "eventsource": "^2.0.2", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://tidelift.com/funding/github/npm/sockjs-client" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -18663,6 +20018,15 @@ "node": ">= 0.8" } }, + "node_modules/stompjs": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/stompjs/-/stompjs-2.3.3.tgz", + "integrity": "sha512-5l/Ogz0DTFW7TrpHF0LAETGqM/so8UxNJvYZjJKqcX31EVprSQgnGkO80tZctPC/lFBDUrSFiTG3xd0R27XAIA==", + "license": "Apache-2.0", + "optionalDependencies": { + "websocket": "latest" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", @@ -18684,6 +20048,11 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==" + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -18945,6 +20314,75 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.12.tgz", + "integrity": "sha512-n/O4PzRPhbYI0k1vKKayfti3C/IGcPf+DqcrOB7O/ab9x4u/zjqraneT5N45+sIe87cxrCApXM8Bna7NYxwoTA==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.38", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -19423,6 +20861,14 @@ "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", "license": "MIT" }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "engines": { + "node": ">=12.22" + } + }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -19456,6 +20902,11 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -19573,6 +21024,13 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "license": "0BSD" }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC", + "optional": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -19910,6 +21368,20 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -20015,6 +21487,14 @@ "makeerror": "1.0.12" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/watchpack": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", @@ -20181,6 +21661,30 @@ } } }, + "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, "node_modules/webpack-dev-server/node_modules/ws": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", @@ -20289,6 +21793,24 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/websocket": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", + "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.63", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -20312,6 +21834,23 @@ "node": ">=0.8.0" } }, + "node_modules/websocket/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/websocket/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true + }, "node_modules/whatwg-encoding": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", @@ -20946,6 +22485,16 @@ "node": ">=10" } }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.32" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/Front-feedme/package.json b/Front-feedme/package.json index 1d9e1ff..f70912b 100644 --- a/Front-feedme/package.json +++ b/Front-feedme/package.json @@ -5,27 +5,41 @@ "dependencies": { "@emotion/react": "^11.13.0", "@emotion/style": "^0.8.0", - "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@emotion/styled": "^11.13.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", - "@mui/material": "^5.16.4", - "@reduxjs/toolkit": "^2.2.6", + "@mui/icons-material": "^5.16.6", + "@mui/material": "^5.16.6", + "@reduxjs/toolkit": "^2.2.7", + "@stomp/stompjs": "^7.0.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "axios": "^1.7.2", + "antd": "^5.20.0", + "axios": "^1.7.3", + "date-fns": "^3.6.0", + "dayjs": "^1.11.12", + "event-source-polyfill": "^1.0.31", "framer-motion": "^11.3.12", + "http-proxy-middleware": "^3.0.0", "qs": "^6.12.3", "quill": "^2.0.2", "react": "^18.3.1", + "react-calendar": "^5.0.0", "react-dom": "^18.3.1", "react-helmet-async": "^2.0.5", + "react-icons": "^5.2.1", + "react-modal": "^3.16.1", "react-redux": "^9.1.2", "react-router-dom": "^6.25.1", "react-scripts": "5.0.1", "react-social-login-buttons": "^4.1.0", "redux-saga": "^1.3.0", "sanitize-html": "^2.13.0", + "sockjs-client": "^1.6.1", + "stompjs": "^2.3.3", + "styled-components": "^6.1.12", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/Front-feedme/public/images/img-cat.png b/Front-feedme/public/images/img-cat.png new file mode 100644 index 0000000..cc9280f Binary files /dev/null and b/Front-feedme/public/images/img-cat.png differ diff --git a/Front-feedme/public/images/thumbnail1.png b/Front-feedme/public/images/thumbnail1.png new file mode 100644 index 0000000..3ed75d3 Binary files /dev/null and b/Front-feedme/public/images/thumbnail1.png differ diff --git a/Front-feedme/public/images/thumbnail2.jpg b/Front-feedme/public/images/thumbnail2.jpg new file mode 100644 index 0000000..3f964b3 Binary files /dev/null and b/Front-feedme/public/images/thumbnail2.jpg differ diff --git a/Front-feedme/public/images/thumbnail3.png b/Front-feedme/public/images/thumbnail3.png new file mode 100644 index 0000000..6073d8e Binary files /dev/null and b/Front-feedme/public/images/thumbnail3.png differ diff --git a/Front-feedme/public/index.html b/Front-feedme/public/index.html index 4acf4c3..8af4dd0 100644 --- a/Front-feedme/public/index.html +++ b/Front-feedme/public/index.html @@ -4,6 +4,7 @@ + React App diff --git a/Front-feedme/src/App.js b/Front-feedme/src/App.js index c0e1d3e..b923848 100644 --- a/Front-feedme/src/App.js +++ b/Front-feedme/src/App.js @@ -5,13 +5,17 @@ import Intro from './pages/Intro.jsx'; import Login from './pages/Login.jsx'; import Signup from './components/Login/Signup.jsx'; import CreatureCreate from './components/Creature/CreatureCreate.jsx' -import CreatureResult from './components/Creature/CreatureResult.jsx'; +import CreatureResult from './components/Creature/CreatureResult.jsx' import Main from './pages/Main.jsx'; import Todo from './components/Todo/Todo.jsx'; import Diary from './components/Diary/Diary.jsx'; import Chat from './components/Chatting/Chat.jsx'; import Setting from './components/Set/Setting.jsx'; import MyPage from './components/Mypage/MyPage.jsx'; +import Feed from './components/Feed/Feed.jsx'; +import Weather from './components/Weather/Weather.jsx'; +import LoginLoding from './components/Login/LoginLoding.jsx'; +import WebSocketChat from './components/WebScoketChat/WebSocketChat.jsx'; function App() { return ( @@ -38,6 +42,10 @@ function App() { } /> } /> } /> + } /> + } /> + } /> + } /> ); diff --git a/Front-feedme/src/api/Api.js b/Front-feedme/src/api/Api.js new file mode 100644 index 0000000..1ef6c76 --- /dev/null +++ b/Front-feedme/src/api/Api.js @@ -0,0 +1,27 @@ +import axios from 'axios'; + +const API = axios.create({ + BASE_URL: 'https://i11b104.p.ssafy.io/api/', + headers: { + 'Content-Type': 'application/json', + }, + withCredentials: true, +}); + +// 요청 인터셉터 설정 : 요청 전 실행 +API.interceptors.request.use( + config => { + const token = sessionStorage.getItem('accessToken'); + if (token) { + config.headers['Authorization'] = token; // Bearer 없이 토큰 설정 + } else { + console.log('토큰이 없습니다.'); + } + return config; + }, + error => { + return Promise.reject(error); + } +); + +export default API; \ No newline at end of file diff --git a/Front-feedme/src/api/userApi.js b/Front-feedme/src/api/userApi.js deleted file mode 100644 index e7c3b09..0000000 --- a/Front-feedme/src/api/userApi.js +++ /dev/null @@ -1 +0,0 @@ -// 소셜 로그인이라 필요 유무 고민 중 \ No newline at end of file diff --git a/Front-feedme/src/assets/icons/icon-account-gray-24.png b/Front-feedme/src/assets/icons/icon-account-gray-24.png new file mode 100644 index 0000000..18bb861 Binary files /dev/null and b/Front-feedme/src/assets/icons/icon-account-gray-24.png differ diff --git a/Front-feedme/src/assets/icons/icon-search-gray-24.png b/Front-feedme/src/assets/icons/icon-search-gray-24.png new file mode 100644 index 0000000..fa832dd Binary files /dev/null and b/Front-feedme/src/assets/icons/icon-search-gray-24.png differ diff --git a/Front-feedme/src/assets/images/img-cat.png b/Front-feedme/src/assets/images/img-cat.png new file mode 100644 index 0000000..cc9280f Binary files /dev/null and b/Front-feedme/src/assets/images/img-cat.png differ diff --git a/Front-feedme/src/assets/images/img-main.png b/Front-feedme/src/assets/images/img-main.png new file mode 100644 index 0000000..ce680ca Binary files /dev/null and b/Front-feedme/src/assets/images/img-main.png differ diff --git a/Front-feedme/src/components/Buttons/KakaoButton.jsx b/Front-feedme/src/components/Buttons/KakaoButton.jsx index 86069bc..00a2406 100644 --- a/Front-feedme/src/components/Buttons/KakaoButton.jsx +++ b/Front-feedme/src/components/Buttons/KakaoButton.jsx @@ -2,11 +2,17 @@ import React from "react"; import { createButton } from "react-social-login-buttons"; import kakaoLogo from "../../assets/images/kakao-logo.png"; +const handleKakaoLogin = () => { + // window.location.href = 'https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=e76f1a4a0c79727d85cd6b3b1969c892&redirect_uri=http://localhost:3000/login/oauth2/code/kakao'; + window.location.href = 'https://i11b104.p.ssafy.io/api/oauth2/authorization/kakao'; +}; + const config = { text: "Continue with Kakao", icon: (props) => Kakao, style: { background: "#F7E600", color: '#000000', display: 'flex', alignItems: 'center', justifyContent: 'center' }, activeStyle: { background: "#E1D100" }, + onClick: handleKakaoLogin, }; /** My Naver login button. */ diff --git a/Front-feedme/src/components/Buttons/NaverButton.jsx b/Front-feedme/src/components/Buttons/NaverButton.jsx index 458827d..eda13d4 100644 --- a/Front-feedme/src/components/Buttons/NaverButton.jsx +++ b/Front-feedme/src/components/Buttons/NaverButton.jsx @@ -2,11 +2,17 @@ import React from "react"; import { createButton } from "react-social-login-buttons"; import naverLogo from "../../assets/images/naver-logo.png"; +const handleNaverLogin = () => { + // window.location.href = 'https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=QdiZgbrsCQdA7Zw1pJNN&redirect_uri=http://localhost:8080/login/oauth2/code/naver&state=RANDOM_STATE'; + window.location.href = 'https://i11b104.p.ssafy.io/api/oauth2/authorization/naver'; +}; + const config = { text: "Continue with Naver", icon: (props) => Naver, style: { background: "#2DB400", color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center' }, activeStyle: { background: "#1E7900" }, + onClick: handleNaverLogin, }; /** My Naver login button. */ diff --git a/Front-feedme/src/components/Chatting/Chat.css b/Front-feedme/src/components/Chatting/Chat.css index 39a5df6..87289ed 100644 --- a/Front-feedme/src/components/Chatting/Chat.css +++ b/Front-feedme/src/components/Chatting/Chat.css @@ -1,52 +1,147 @@ -.ChatContainer { - display: flex; +.ChatBack{ width: 100%; height: 100vh; - border: solid 1px; + display: flex; + justify-content: center; + align-items: center; + position: relative; + overflow-y: hidden; } -.ChatMain { +.ChatBack::before { + content: ""; + background: url('../../assets/images/img-main.png') no-repeat center; + background-size: cover; + opacity: 0.8; + position: absolute; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + overflow: hidden; + /* overflow-y: scroll; */ display: flex; - flex-direction: column; - width: 100%; - border: solid 2px; + justify-content: center; + align-items: center; +} + +.ChatBox{ + width: 83%; + height: 85%; + border-radius: 20px; + background-color: rgba(255, 255, 255, 0.4); + backdrop-filter: blur(12px); + display: flex; + justify-content: flex-start; + align-items: center; + position: relative; } -.ChatDashboard{ - display: flex; - flex: 1; - justify-content: space-around; - padding: 20px; - border: solid 1px; +.ChatRight{ + width: 80%; + height: 91%; + display: flex; + flex-direction: column-reverse; + + overflow: hidden; +} + +.ChatRightContents{ + display: flex; + justify-content: flex-start; + margin-top: 25px; + margin-right: 3%; + height: 93%; + border-radius: 20px; + background-color: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(12px); + + overflow: hidden; } .ChatFriendList { display: flex; - flex :1; + flex :0.4; overflow-y: auto; + /* border: solid red 3px; */ } -.ChatFriendItem { +.ChatDetail { + flex :0.6; + height: 100%; + overflow: hidden; display: flex; + justify-content: center; align-items: center; - padding: 10px; - border-bottom: 1px solid #ccc; } -.ChatFriendItem img { - width: 40px; - height: 40px; - border-radius: 50%; - margin-right: 10px; +.ChatDetailInner{ + background-color: rgba(255, 255, 255, 0.6); + border-radius: 20px; + width: 90%; + height: 90%; + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; + + flex-direction: column; } -.ChatFriendInfo { +.ChatModal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); display: flex; - flex-direction: column; + justify-content: center; + align-items: center; + z-index: 1000; } -.ChatDetail { - flex :1; - padding: 20px; - background-color: #f0f0f0; +.ChatModalContent { + background: white; + padding: 2%; + border-radius: 40px; + text-align: center; + font-family: 'PretendardB'; + font-size: 1.3rem; } + +.ChatModalContentCancel { + background-color: #808080; + color: #FFFFFF; + margin: 5%; + padding: 3% 7%; + border: none; + border-radius: 20px; + cursor: pointer; + font-family: 'PretendardR'; + font-size: 1rem; + transition: background-color 0.3s ease, transform 0.2s ease; +} + +.ChatModalContentCancel:hover { + background-color: #696969; + transform: scale(1.05); +} + +.ChatModalContentDel { + background-color: #FF5F5F; + color: #FFFFFF; + margin: 5%; + padding: 3% 7%; + border: none; + border-radius: 20px; + cursor: pointer; + font-family: 'PretendardR'; + font-size: 1rem; + transition: background-color 0.3s ease, transform 0.2s ease; +} + +.ChatModalContentDel:hover { + background-color: #CC0000; + transform: scale(1.05); +} \ No newline at end of file diff --git a/Front-feedme/src/components/Chatting/Chat.jsx b/Front-feedme/src/components/Chatting/Chat.jsx index fc457ec..8df7910 100644 --- a/Front-feedme/src/components/Chatting/Chat.jsx +++ b/Front-feedme/src/components/Chatting/Chat.jsx @@ -1,24 +1,57 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; +import { setToken } from '../../store/slice'; +import { fetchUserData } from '../../store/userSlice'; +import { fetchFriendsList } from '../../store/friendsSlice'; +import { fetchFriendInfo } from '../../store/friendInfoSlice'; import Sidebar from '../Main/Sidebar'; import Search from '../Main/Search'; import ChattingFriendList from './ChattingFriendList'; import ChattingFriendProfile from './ChattingFriendProfile'; import ChatWindow from './ChatWindow'; +import Creature from '../Mypage/Creature'; import './Chat.css'; -import test1 from '../../assets/images/test1.png'; +import '../../assets/font/Font.css'; const Chat = () => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + + useEffect(() => { + const sessionToken = sessionStorage.getItem('accessToken'); + if (sessionToken) { + dispatch(setToken(sessionToken)); + } else { + navigate('/login'); + } + }, [dispatch, navigate]); + + const token = useSelector((state) => state.auth.token); + const user = useSelector((state) => state.user); + const friendsList = useSelector((state) => state.friendsList.list); + const selectedFriendInfo = useSelector((state) => state.friendInfo); + + const { creatureId, creatureName, exp, level, image, togetherDay, error } = user; + + useEffect(() => { + if (token) { + dispatch(fetchUserData(token)); + dispatch(fetchFriendsList(token)); + } + }, [dispatch, token]); + const [selectedFriend, setSelectedFriend] = useState(null); const [view, setView] = useState('profile'); - - const friends = [ - { id: 1, name: 'pi', avatar: test1 }, - { id: 2, name: '지나', avatar: test1 }, - ]; + const [isModalOpen, setIsModalOpen] = useState(false); + const [friendToDelete, setFriendToDelete] = useState(null); const handleFriendClick = (friend) => { setSelectedFriend(friend); - setView('profile'); + setView(friend.id === 'my-avatar' ? 'creature' : 'profile'); + if (friend.id !== 'my-avatar') { + dispatch(fetchFriendInfo({ token, counterpartNickname: friend.counterpartNickname })); + } }; const handleChatClick = (friend) => { @@ -26,34 +59,66 @@ const Chat = () => { setView('chat'); }; + const handleDeleteFriend = (friend) => { + setFriendToDelete(friend); + setIsModalOpen(true); + }; + + const confirmDeleteFriend = () => { + setSelectedFriend(null); + setIsModalOpen(false); + }; + + const closeModal = () => { + setIsModalOpen(false); + setFriendToDelete(null); + }; + return ( -
- - -
- -
-
- -
+
+
+ +
+
+
+ +
-
- {selectedFriend ? ( - view === 'profile' ? ( - - ) : ( - - ) - ) : ( -
친구를 선택하세요
- )} -
+
+ {selectedFriend ? ( +
+ {view === 'profile' ? ( + + ) : view === 'chat' ? ( + + ) : ( + + )} +
+ ) : ( +
친구를 선택하세요
+ )} +
+
+
+ + {isModalOpen && ( +
+
+

정말로 친구를 삭제하시겠습니까?

+ + +
+
+ )}
); }; diff --git a/Front-feedme/src/components/Chatting/ChattingFriendList.css b/Front-feedme/src/components/Chatting/ChattingFriendList.css new file mode 100644 index 0000000..f648349 --- /dev/null +++ b/Front-feedme/src/components/Chatting/ChattingFriendList.css @@ -0,0 +1,84 @@ + .ChatFriendListContainer { + width: 100%; + } + + .ChatFriendSearch { + width: 100%; + padding: 1rem; + border-radius: 25px; + border: none; + margin-bottom: 1%; + box-sizing: border-box; + font-family: 'PretendardR'; + font-size: 1rem; + line-height: 1.5; + } + + .ChatFriendList { + display: flex; + flex-direction: column; + max-height: 100%; + overflow-y: auto; + padding-top: 4%; + padding-left: 3%; + padding-right: 3%; + } + + .ChatFriendList::-webkit-scrollbar { + width: 10px; + } + + .ChatFriendList::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.5); + border-radius: 20px; + } + + .ChatFriendList::-webkit-scrollbar-thumb:hover { + background: #555; + } + + .ChatFriendItem { + display: flex; + align-items: center; + padding: 10px; + border-bottom: 1px solid #cdcdcd; + } + + .ChatFriendAvatar { + width: 40px; + height: 40px; + border-radius: 50%; + margin-right: 10px; + } + + .ChatFriendInfo { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + } + + .ChatFriendName { + cursor: pointer; + color: #000; + margin-right: auto; + font-family: 'PretendardR'; + font-size: 1.2rem; + padding-left: 3%; + } + + + + + .ChatIconButton { + background: none; + border: none; + cursor: pointer; + color: #000; + margin-left: 10px; + } + + .ChatIconButton:hover, + .ChatIconButton.active { + color: #9E69FA; + } diff --git a/Front-feedme/src/components/Chatting/ChattingFriendList.jsx b/Front-feedme/src/components/Chatting/ChattingFriendList.jsx index e7c9bb7..0f54d85 100644 --- a/Front-feedme/src/components/Chatting/ChattingFriendList.jsx +++ b/Front-feedme/src/components/Chatting/ChattingFriendList.jsx @@ -1,20 +1,73 @@ -import React from 'react'; +import React, { useState } from 'react'; +import { useSelector } from 'react-redux'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faMessage } from '@fortawesome/free-solid-svg-icons'; +import './ChattingFriendList.css'; +import '../../assets/font/Font.css'; const ChattingFriendList = ({ friends, onFriendClick, onChatClick }) => { + const [searchTerm, setSearchTerm] = useState(''); + const [activeFriendId, setActiveFriendId] = useState(null); + + const user = useSelector((state) => state.user); + + const handleSearchChange = (e) => { + setSearchTerm(e.target.value); + }; + + const handleChatButtonClick = (friend) => { + setActiveFriendId(friend.id); + onChatClick(friend); + }; + + const filteredFriends = friends.filter(friend => + friend.counterpartNickname.toLowerCase().includes(searchTerm.toLowerCase()) + ); + return ( -
- {friends.map(friend => ( -
- {friend.name} +
+ +
+
+ 내 아바타
- {friend.name} - - + onFriendClick({ id: 'my-avatar', name: user.nickname, avatar: user.image })} className="ChatFriendName"> + {user.nickname} + +
- ))} + + {filteredFriends.map(friend => ( +
+ {friend.counterpartNickname} +
+ onFriendClick(friend)} className="ChatFriendName"> + {friend.counterpartNickname} + + +
+
+ ))} +
); }; -export default ChattingFriendList; +export default ChattingFriendList; \ No newline at end of file diff --git a/Front-feedme/src/components/Chatting/ChattingFriendProfile.css b/Front-feedme/src/components/Chatting/ChattingFriendProfile.css new file mode 100644 index 0000000..68a02d7 --- /dev/null +++ b/Front-feedme/src/components/Chatting/ChattingFriendProfile.css @@ -0,0 +1,113 @@ +.CFProfile { + text-align: center; + border-radius: 10px; + padding: 20px; + /* width: 200px; */ + margin: 0 auto; + font-family: 'PretendardR'; + + width: 75%; + display: flex; + flex-direction: column; + align-items: center; + position: relative; +} + +.CFProfileDeleteIcon { + position: absolute; + right: 1%; + cursor: pointer; +} + +.CFProfileName { + font-family: 'PretendardSB'; + font-size: 1.5rem; + margin-bottom: 0; + margin-top: 3%; +} + +.CFProfileterm{ + margin-top: 3%; + margin-bottom: 3%; +} + +.CFProfileLv { + margin-top: 3%; +} + +.CFProfileImage { + width: 65%; + height: auto; + border-radius: 50%; + object-fit: cover; + margin: 0 auto; +} + +.CFProfileInfo { + margin-top: 1%; + text-align: center; + font-family: 'PretendardSB'; + + width: 100%; + margin-bottom: 3%; +} + +.CFProfileExp { + display: flex; + align-items: center; + justify-content: center; + position: relative; + height: 2rem; + width: 100%; + border-radius: 10px; + overflow: hidden; + background-color: #7A7A7A80; + margin-top: 10px; + font-family: 'PretendardR'; +} + +.CFProfileExp p { + position: absolute; + left: 10px; + margin: 0; + padding: 0; + font-size: 14px; + color: #666; + z-index: 1; +} + +.CFProfileExp progress { + width: 100%; + height: 100%; + -webkit-appearance: none; + appearance: none; + border: none; + border-radius: 10px; + overflow: hidden; + background-color: #7A7A7A80; + position: absolute; + top: 0; + left: 0; +} + +.CFProfileExp progress::-webkit-progress-value { + background-color: #BDFF00; + border-radius: 10px 0 0 10px; +} + +@media (max-width: 768px) { + .CFProfileName { + font-size: 1rem; + } + + .CFProfileImage { + width: 50%; + } + + .CFProfileInfo{ + width: 50%; + } + .CFProfileExp p { + font-size: 12px; + } +} \ No newline at end of file diff --git a/Front-feedme/src/components/Chatting/ChattingFriendProfile.jsx b/Front-feedme/src/components/Chatting/ChattingFriendProfile.jsx index ebf9e8a..bdadad4 100644 --- a/Front-feedme/src/components/Chatting/ChattingFriendProfile.jsx +++ b/Front-feedme/src/components/Chatting/ChattingFriendProfile.jsx @@ -1,20 +1,33 @@ -import React from 'react'; +import React, {useEffect}from 'react'; +import './ChattingFriendProfile.css'; +import '../../assets/font/Font.css'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTrashCan } from '@fortawesome/free-solid-svg-icons'; -const ChattingFriendProfile = ({ friend }) => { - - // 이미지 사이즈 조정 삭제해도 됨 - const sample = { - width:"100px", - height:'100px', - objectFit:"cover" - } +const ChattingFriendProfile = ({ friend, onDelete }) => { + useEffect(() => { + // console.log('Selected Friend Info:', friend); // 친구 정보를 콘솔에 출력 + }, [friend]); return ( -
-

{friend.name}

- {friend.name} -

Level: 1

-

EXP: 305

+
+ onDelete(friend)} + /> + +

{friend.creatureNickname}

+

🤍 {friend.join}일째 함께하는 중

+ {friend.nickname} +
+

Lv. {friend.level}

+
+

EXP

+ +
+
); }; diff --git a/Front-feedme/src/components/Creature/CreatureCreate.css b/Front-feedme/src/components/Creature/CreatureCreate.css index ed96658..7dddec2 100644 --- a/Front-feedme/src/components/Creature/CreatureCreate.css +++ b/Front-feedme/src/components/Creature/CreatureCreate.css @@ -10,7 +10,7 @@ .CreatureCreateMain::before { content: ''; - background: url('../../assets/images/test2.png') no-repeat center; + background: url('../../assets/images/img-main.png') no-repeat center; background-size: cover; opacity: 0.7; position: absolute; @@ -51,6 +51,7 @@ padding-top: 3%; padding-left: 25%; padding-right: 25%; + overflow-y: auto; } .CreatureCreateFormNicknameContainer { @@ -73,7 +74,6 @@ border-radius: 10px; font-size: 1em; height: 75%; - margin-left: 8%; font-family: 'PretendardR'; } @@ -142,7 +142,7 @@ } .CreatureCreateButton { - background-color: #7DCE13; + background-color: #FF5F5F; color: white; padding: 1% 3%; border: none; @@ -156,7 +156,7 @@ } .CreatureCreateButton:hover { - background-color: #6BB012; + background-color: #d13939; } .backButton { diff --git a/Front-feedme/src/components/Creature/CreatureCreate.jsx b/Front-feedme/src/components/Creature/CreatureCreate.jsx index 0a7899f..6d54e80 100644 --- a/Front-feedme/src/components/Creature/CreatureCreate.jsx +++ b/Front-feedme/src/components/Creature/CreatureCreate.jsx @@ -1,39 +1,99 @@ import React, { useState } from 'react'; -import { Link } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faAngleLeft } from '@fortawesome/free-solid-svg-icons'; +import { useDispatch, useSelector } from 'react-redux'; +import { setCreatureName, setKeyword, addKeyword, removeKeyword, resetKeywords, } from '../../store/slice'; import './CreatureCreate.css'; import '../../assets/font/Font.css'; - -// function Checkbox({ children, disabled, checked, onChange }) { -// return ( -// -// ); -// } +import axios from 'axios'; const CreatureCreate = () => { - const [nickname, setNickname] = useState(''); - const [selectedKeywords, setSelectedKeywords] = useState([]); - const [image, setImage] = useState(null); - - const handleKeywordChange = (keyword) => { - setSelectedKeywords((prevKeywords) => - prevKeywords.includes(keyword) - ? prevKeywords.filter((kw) => kw !== keyword) - : [...prevKeywords, keyword] - ); - }; + const navigate = useNavigate(); + const dispatch = useDispatch(); + const { creatureName, keyword, token } = useSelector((state) => state.auth); + const [photo, setPhoto] = useState(null); // local state로 photo 관리 const handleImageUpload = (e) => { - setImage(URL.createObjectURL(e.target.files[0])); + const fileUrl = e.target.files[0]; + if (fileUrl) { + setPhoto(fileUrl); // 로컬 상태에 파일 저장 + } + }; + + // const handleKeywordChange = (type, value) => { + // const keyword = { type, value }; + // const isKeywordExist = keywords.some((kw) => kw.type === type); + + // if (isKeywordExist) { + // // 이미 존재하는 키워드 타입을 제거하고 새로운 값으로 추가 + // dispatch(removeKeyword({ type })); + // } + + // dispatch(addKeyword(keyword)); + // }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + // const formData = new FormData(); + // formData.append('creatureName', creatureName); + // formData.append('keywords', JSON.stringify(keywords)); // 키워드 배열 직렬화 + // if (photo) { + // formData.append('photo', photo.name); // 파일 추가 + // } + + // const formData = new FormData(); + // formData.append('creatureName', creatureName); + // formData.append('photo', photo.name); + // formData.append('keyword', keyword); + + // // FormData를 JSON으로 변환하는 함수 + // function formDataToJson(formData) { + // const obj = {}; + // formData.forEach((value, key) => { + // if (value instanceof File) { + // // 파일 메타데이터를 JSON으로 포함 + // obj[key] = { + // name: value.name, + // size: value.size, + // type: value.type, + // }; + // } else { + // obj[key] = value; + // } + // }); + // return JSON.stringify(obj); + // } + + // const json = formDataToJson(formData); + + console.log(photo.name); + console.log(creatureName); + console.log(keyword); + console.log(token); + + // 서버에 JSON 형식으로 데이터 보내기 + try { + const response = await axios.post('https://i11b104.p.ssafy.io/api/creature', { + creatureName, + keyword, + photo: photo.name + }, { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `${token}`, + } + }); + + if (response.status === 200) { + navigate('/CreatureResult'); + } else { + console.log('크리쳐 생성 실패', response.data); + } + } catch (error) { + console.error('서버 요청 중 오류 발생', error); + } }; return ( @@ -43,20 +103,20 @@ const CreatureCreate = () => {
아바타 생성하기
-
+
- + setNickname(e.target.value)} + value={creatureName} + onChange={(e) => dispatch(setCreatureName(e.target.value))} />
- + { />
- {image && ( + {photo && (
- Preview + Preview
)} -
- + {/*
+
-
- handleKeywordChange('cute')} - /> - +
+
개체 선택
+
+ handleKeywordChange('type', e.target.value)} + /> + +
+
+ handleKeywordChange('type', e.target.value)} + /> + +
+
+ handleKeywordChange('type', e.target.value)} + /> + +
+
+ handleKeywordChange('type', e.target.value)} + /> + +
+
+ handleKeywordChange('type', e.target.value)} + /> + +
+
+ handleKeywordChange('type', e.target.value)} + /> + +
-
- handleKeywordChange('innocent')} - /> - -
-
- handleKeywordChange('bright')} - /> - -
-
- handleKeywordChange('joy')} - /> - +
+
색상 선택
+
+ handleKeywordChange('color', e.target.value)} + /> + +
+
+ handleKeywordChange('color', e.target.value)} + /> + +
+
+ handleKeywordChange('color', e.target.value)} + /> + +
+
+ handleKeywordChange('color', e.target.value)} + /> + +
+
+ handleKeywordChange('color', e.target.value)} + /> + +
+
+ handleKeywordChange('color', e.target.value)} + /> + +
+
+ handleKeywordChange('color', e.target.value)} + /> + +
+
+ handleKeywordChange('color', e.target.value)} + /> + +
-
- - - - +
*/} +
diff --git a/Front-feedme/src/components/Creature/CreatureResult.css b/Front-feedme/src/components/Creature/CreatureResult.css index 5b5afbd..6670d50 100644 --- a/Front-feedme/src/components/Creature/CreatureResult.css +++ b/Front-feedme/src/components/Creature/CreatureResult.css @@ -10,7 +10,7 @@ .CreatureResultMain::before { content: ''; - background: url('../../assets/images/test2.png') no-repeat center; + background: url('../../assets/images/img-main.png') no-repeat center; background-size: cover; opacity: 0.7; position: absolute; @@ -53,7 +53,7 @@ } .CreatureResultNameContainer { - background-color: rgb(135, 201, 8, 0.2); + background-color: rgba(255, 95, 95, 0.13); border-radius: 15px; margin-bottom: 1em; display: flex; @@ -73,7 +73,7 @@ } .CreatureResultStartButton { - background-color: #7DCE13; + background-color: #FF5F5F; color: white; padding: 1% 3%; border: none; @@ -87,7 +87,7 @@ } .CreatureResultStartButton:hover { - background-color: #6BB012; + background-color: #d13939; } .CreatureResultReloadButton { diff --git a/Front-feedme/src/components/Creature/CreatureResult.jsx b/Front-feedme/src/components/Creature/CreatureResult.jsx index 090a612..705875c 100644 --- a/Front-feedme/src/components/Creature/CreatureResult.jsx +++ b/Front-feedme/src/components/Creature/CreatureResult.jsx @@ -4,29 +4,33 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faAngleLeft, faRotate } from '@fortawesome/free-solid-svg-icons'; import './CreatureResult.css'; import '../../assets/font/Font.css'; -import catImage from '../../assets/images/test3.png'; +import { useDispatch, useSelector } from 'react-redux'; +import catImage from '../../assets/images/test3.png'; const CreatureResult = () => { + + const { creatureName, photo } = useSelector((state) => state.auth); + return (
-
-
냐옹이
-
+
+
{creatureName}
+
- Cat + Cat
- + {/* - + */}
); diff --git a/Front-feedme/src/components/Diary/Diary.css b/Front-feedme/src/components/Diary/Diary.css index a949fd7..c39b911 100644 --- a/Front-feedme/src/components/Diary/Diary.css +++ b/Front-feedme/src/components/Diary/Diary.css @@ -1,20 +1,53 @@ -.DiaryContainer { +.Diary { + width: 100%; + height: 100vh; display: flex; - border: solid 1px; -} + justify-content: center; + align-items: center; + position: relative; + } -.DiaryMain { - flex: 1; + .Diary::before { + content: ""; + background: url("../../assets/images/img-main.png") no-repeat center; + background-size: cover; + opacity: 0.8; + position: absolute; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + overflow: hidden; + /* overflow-y: scroll; */ display: flex; - flex-direction: column; - border: solid 1px; -} + justify-content: center; + align-items: center; + } - -.DiaryDashboard { + .DiaryContainer { + width: 83%; + height: 85%; + border-radius: 20px; + background-color: rgba(255, 255, 255, 0.4); + backdrop-filter: blur(12px); display: flex; - flex: 1; - justify-content: space-around; - padding: 20px; - border: solid 1px; -} + justify-content: flex-start; + align-items: center; + position: relative; + } + + .DiaryMain { + width: 80%; + height: 91%; + display: flex; + + flex-direction: column-reverse; + } + + .DiaryDashboard { + display: flex; + justify-content: flex-start; + margin-top: 25px; + height: 93%; + } + \ No newline at end of file diff --git a/Front-feedme/src/components/Diary/Diary.jsx b/Front-feedme/src/components/Diary/Diary.jsx index c052988..3f8958e 100644 --- a/Front-feedme/src/components/Diary/Diary.jsx +++ b/Front-feedme/src/components/Diary/Diary.jsx @@ -1,21 +1,24 @@ -import React from 'react'; -import Sidebar from '../Main/Sidebar'; -import Search from '../Main/Search'; -import DiaryPic from './DiaryPic'; -import './Diary.css' +import React from "react"; +import Sidebar from "../Main/Sidebar"; +import SearchBar from "../Main/Search"; +import DiaryPic from "./DiaryPic"; +import "./Diary.css"; const Diary = () => { return ( -
- -
- +
+
+ +
+ + +
); }; -export default Diary; \ No newline at end of file +export default Diary; diff --git a/Front-feedme/src/components/Diary/DiaryPic.css b/Front-feedme/src/components/Diary/DiaryPic.css new file mode 100644 index 0000000..8f97950 --- /dev/null +++ b/Front-feedme/src/components/Diary/DiaryPic.css @@ -0,0 +1,78 @@ +.DiaryPic { + border-radius: 20px; + background-color: rgba(255, 255, 255, 0.3); + backdrop-filter: blur(12px); + width: 97%; + font-family: 'PretendardR'; + /* display: flex; + flex-direction: column; */ + /* z-index: -1; */ +} + +.DiaryPicDay { + display: flex; + justify-content: center; + margin-top: 15px; + margin-bottom: 0; + font-family: 'PretendardSB'; +} + +.DiaryPicContents { + text-align: center; + padding: 10px; +} + +.DiaryArrows:hover { + transform: translateY(-4px); +} + +.CreateFeedContainer { + display: flex; + justify-content: flex-end; + margin-top: 10px; + margin-right: 30px; +} + +.CreateFeedButton { + border: none; + background-color: white; + color: black; + border-radius: 30px; + font-family: 'PretendardR'; + cursor: pointer; + display: flex; + align-items: center; + height: 2rem; + justify-content: center; + width: 120px; +} + +.CreateFeedIcon { + margin-right: 0.5rem; +} + +.TodoMainModalButton { + background-color: #808080; + color: white; + border: none; + padding: 10px 20px; + border-radius: 5px; + cursor: pointer; + font-size: 1em; +} + +.TodoMainModalButton:last-child { + background-color: #9E69FA; +} + +.DrawingModalDImage { + width: 80%; + height: 30%; + object-fit: cover; + margin: 0 auto; +} + +.DrawingContent { + min-height: 200px; + font-family: 'PretendardR'; +} \ No newline at end of file diff --git a/Front-feedme/src/components/Diary/DiaryPic.jsx b/Front-feedme/src/components/Diary/DiaryPic.jsx index d01ad30..132d7b3 100644 --- a/Front-feedme/src/components/Diary/DiaryPic.jsx +++ b/Front-feedme/src/components/Diary/DiaryPic.jsx @@ -1,10 +1,295 @@ -import React from 'react'; +import React, { useState, useEffect } from "react"; +import styled from "styled-components"; +import './DiaryPic.css'; +import UploadOutlinedIcon from '@mui/icons-material/UploadOutlined'; +import Modal from 'react-modal'; +import axios from 'axios'; + +function DiaryPic() { + const [slideIndex, setSlideIndex] = useState(0); + const [currentDate, setCurrentDate] = useState(""); + const [currentContent, setCurrentContent] = useState(""); + const [drawingModalIsOpen, setDrawingModalIsOpen] = useState(false); + const [selectedImage, setSelectedImage] = useState(""); // 선택된 이미지를 저장하는 상태 + const [diaries, setDiaries] = useState([]); // 다이어리 데이터를 저장할 상태 + const [page, setPage] = useState(0); // 현재 페이지 상태 + const [limit, setLimit] = useState(10); // 페이지 당 항목 수 + const [totalPages, setTotalPages] = useState(1); // 전체 페이지 수 + const [feedContent, setFeedContent] = useState(""); // 피드 텍스트 내용을 저장하는 상태 + + useEffect(() => { + fetchDiaries(page, limit); + }, [page, limit]); + + const fetchDiaries = async (page, limit) => { + try { + const response = await axios.post('https://i11b104.p.ssafy.io/api/diary/list', { + skip: page * limit, + limit: limit, + }, + { + headers: { + 'Content-Type': 'application/json', + 'Authorization': sessionStorage.getItem('accessToken'), + } + }); + + console.log('pages:', response.data.content); + + const sortedDiaries = response.data.content; + const date = sortedDiaries[0]?.createdAt.split('T')[0]; + + setDiaries(sortedDiaries); + setTotalPages(response.data.totalPages); + setCurrentDate(date || ""); + setCurrentContent(sortedDiaries[0]?.content || ""); + setSlideIndex(0); + } catch (error) { + console.error('Error fetching diaries:', error); + } + }; + + const moveToPrevSlide = async () => { + setSlideIndex((prev) => { + // 현재 페이지의 첫 슬라이드에서 왼쪽으로 이동할 때, 이전 페이지를 요청 + if (prev === 0 && page > 0) { + const prevPage = page - 1; + fetchDiaries(prevPage, limit); // 이전 페이지의 데이터를 가져옴 + setPage(prevPage); + return limit - 1; // 이전 페이지의 마지막 슬라이드로 이동 + } + + const newIndex = prev === 0 ? diaries.length - 1 : prev - 1; + setCurrentDate(diaries[newIndex].createdAt.split('T')[0]); + setCurrentContent(diaries[newIndex].content); + return newIndex; + }); + }; + + const moveToNextSlide = async () => { + setSlideIndex((prev) => { + const newIndex = prev === diaries.length - 1 ? 0 : prev + 1; + + // 현재 페이지의 마지막 슬라이드에서 오른쪽으로 이동할 때, 다음 페이지를 요청 + if (newIndex === 0 && page < totalPages - 1) { + setPage((prevPage) => prevPage + 1); + fetchDiaries(page + 1, limit); // 다음 페이지의 데이터를 가져옴 + return 0; // 다음 페이지의 첫 번째 슬라이드로 이동 + } + + setCurrentDate(diaries[newIndex].createdAt.split('T')[0]); + setCurrentContent(diaries[newIndex].content); + return newIndex; + }); + }; + + const moveDot = (index) => { + setSlideIndex(index); + setCurrentDate(diaries[index].createdAt.split('T')[0]); + setCurrentContent(diaries[index].content); + }; + + const openModalWithImage = () => { + setSelectedImage(diaries[slideIndex].diaryImg); // 현재 슬라이드의 이미지를 선택 + setDrawingModalIsOpen(true); + }; + + const handleUpload = async () => { + if (!selectedImage || !feedContent) { + alert("이미지와 내용을 모두 입력해주세요."); + return; + } + + try { + const response = await axios.post('https://i11b104.p.ssafy.io/api/feed', { + diaryDate: currentDate, + content: feedContent, + }, { + headers: { + 'Content-Type': 'application/json', + 'Authorization': sessionStorage.getItem('accessToken'), + } + }); + + if (response.status === 200) { + alert("피드가 성공적으로 업로드되었습니다."); + setDrawingModalIsOpen(false); + setFeedContent(""); + } else { + alert("피드 업로드에 실패했습니다."); + } + } catch (error) { + console.error('Error uploading feed:', error); + alert("피드 업로드 중 오류가 발생했습니다."); + } + }; -const DiaryPic = () => { - return ( -
그림일기 표시
+
+

{currentDate}

+ + {diaries.map((diary, index) => ( + moveDot(index)} + > + + + ))} + + + ◀ + + + + {diaries.map((diary) => ( + + + + + + ))} + + {currentContent} + + + ▶ + +
+ +
+ + setDrawingModalIsOpen(false)} + contentLabel="피드 올리기" + className="TodoMainModalD" + overlayClassName="TodoMainOverlay" + > +

피드 올리기

+ {selectedImage && ( + 선택된 그림일기 이미지 + )} + +
+ + +
+
+
); -}; +} export default DiaryPic; + + +const Container = styled.div` + width: 500px; + height: 330px; + margin: 0 auto; + overflow: hidden; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-evenly; + font-family: PretendardR; + border-radius: 20px; + background-color: rgba(255, 255, 255, 0.3); + backdrop-filter: blur(12px); +`; + +const Wrapper = styled.div` + width: 100%; + height: 70%; + display: flex; + transition: all 0.3s ease-in-out; + transform: translateX(${({ slideIndex }) => slideIndex * -100 + "%"}); +`; + +const Slide = styled.div` + width: 100%; + height: 100%; + flex-shrink: 0; + display: flex; + justify-content: center; + align-items: center; +`; + +const PhotoWrapper = styled.div` + width: 90%; + height: 100%; + overflow: hidden; + border-radius: 20px; + display: flex; + justify-content: center; + align-items: center; +`; + +const Photo = styled.img` + width: 100%; + height: 100%; + object-fit: cover; +`; + +const Arrow = styled.div` + position: absolute; + top: 0; + bottom: 0; + margin: auto 80px; + left: ${({ direction }) => direction === "prev" && "0px"}; + right: ${({ direction }) => direction === "next" && "0px"}; + width: 45px; + height: 45px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + z-index: 1; + transition: transform 0.3s ease-in-out; +`; + +const ThumbnailContainer = styled.div` + margin: 10px 0; + display: flex; + justify-content: center; +`; + +const ThumbnailWrapper = styled.div` + width: 50px; + height: 50px; + border-radius: 20px; + overflow: hidden; + cursor: pointer; + border: 2px solid transparent; + transition: all 0.3s ease-in-out; + display: flex; + justify-content: center; + align-items: center; + &.active { + width: 55px; + height: 55px; + border-radius: 25px; + background-color: #9E69FA; + } +`; + +const Thumbnail = styled.img` + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 20px; +`; diff --git a/Front-feedme/src/components/Diary/data.js b/Front-feedme/src/components/Diary/data.js new file mode 100644 index 0000000..5bab632 --- /dev/null +++ b/Front-feedme/src/components/Diary/data.js @@ -0,0 +1,74 @@ +const data = [ + { + id: 1, + img: "img-cat.png", + thumbnail: "img-cat.png", + date: "08.01", + content: "오늘은 운동을 하고 코딩 공부를 했다. 그리고 친구를 만났다." + }, + { + id: 2, + img: "thumbnail1.png", + thumbnail: "thumbnail1.png", + date: "08.02", + content: "오늘은 점심을 먹고 코딩 공부를 했다." + }, + { + id: 3, + img: "thumbnail2.jpg", + thumbnail: "thumbnail2.jpg", + date: "08.03", + content: "오늘은 친구를 만났다." + }, + { + id: 4, + img: "thumbnail3.png", + thumbnail: "thumbnail3.png", + date: "08.04", + content: "오늘은 집에서 강아지와 놀았다." + }, + { + id: 4, + img: "thumbnail3.png", + thumbnail: "thumbnail3.png", + date: "08.05", + content: "오늘은 집에서 강아지와 놀았다." + }, + { + id: 4, + img: "thumbnail3.png", + thumbnail: "thumbnail3.png", + date: "08.06", + content: "오늘은 집에서 강아지와 놀았다." + }, + { + id: 4, + img: "thumbnail3.png", + thumbnail: "thumbnail3.png", + date: "08.07", + content: "오늘은 집에서 강아지와 놀았다." + }, + { + id: 4, + img: "thumbnail3.png", + thumbnail: "thumbnail3.png", + date: "08.08", + content: "오늘은 집에서 강아지와 놀았다." + }, + { + id: 4, + img: "thumbnail3.png", + thumbnail: "thumbnail3.png", + date: "08.09", + content: "오늘은 집에서 강아지와 놀았다." + }, + { + id: 4, + img: "thumbnail3.png", + thumbnail: "thumbnail3.png", + date: "08.10", + content: "오늘은 집에서 강아지와 놀았다." + } + ]; + + export default data; \ No newline at end of file diff --git a/Front-feedme/src/components/Feed/Feed.css b/Front-feedme/src/components/Feed/Feed.css new file mode 100644 index 0000000..31ba426 --- /dev/null +++ b/Front-feedme/src/components/Feed/Feed.css @@ -0,0 +1,56 @@ +.FeedBack{ + width: 100%; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + position: relative; + overflow-y: hidden; +} + +.FeedBack::before { + content: ''; + background: url('../../assets/images/img-main.png') no-repeat center; + background-size: cover; + opacity: 0.8; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; + display: flex; + /* z-index: -1; */ + justify-content: center; + align-items: center; +} + +.FeedContainer { + width: 83%; + height: 85%; + border-radius: 20px; + background-color: rgba(255, 255, 255, 0.4); + backdrop-filter: blur(12px); + display: flex; + justify-content: flex-start; + align-items: center; + position: relative; +} + +.FeedMain { + width: 80%; + height: 91%; + display: flex; + flex-direction: column-reverse; +} + +.FeedDashboard { + display: flex; + justify-content: flex-start; + margin-top: 25px; + margin-right: 3%; + height: 93%; + border-radius: 20px; + background-color: rgba(255, 255, 255, 0.6); + backdrop-filter: blur(12px); +} \ No newline at end of file diff --git a/Front-feedme/src/components/Feed/Feed.jsx b/Front-feedme/src/components/Feed/Feed.jsx new file mode 100644 index 0000000..1397e26 --- /dev/null +++ b/Front-feedme/src/components/Feed/Feed.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import Sidebar from '../Main/Sidebar'; +import Search from '../Main/Search'; +import FeedList from './FeedList'; +import './Feed.css' + + +const Feed = () => { + return ( +
+
+ +
+
+ +
+ +
+
+
+ ); +}; + +export default Feed; \ No newline at end of file diff --git a/Front-feedme/src/components/Feed/FeedList.css b/Front-feedme/src/components/Feed/FeedList.css new file mode 100644 index 0000000..ea6acde --- /dev/null +++ b/Front-feedme/src/components/Feed/FeedList.css @@ -0,0 +1,356 @@ +.FeedList { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + position: relative; +} + +.FeedListPhoto { + flex: 0.6; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 60%; + height: auto; + position: relative; +} + +.FeedListCom{ + flex: 0.4; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + padding-top: 5%; + padding-right: 5%; + height: 100%; + overflow-y: hidden; + max-height: 100%; +} + +.FeedListImg { + width: 60%; + height: auto; + object-fit: cover; +} + +.FeedListCaption { + margin-top: 2%; + margin-bottom: 0; + font-size: 1rem; + text-align: center; + font-family: 'PretendardR'; +} + +.arrowleft, .arrowright { + background: none; + border: none; + font-size: 2rem; + cursor: pointer; + position: absolute; + z-index: 100; + top: 50%; + transform: translateY(-50%); +} + +.arrowleft { + left: 0; +} + +.arrowright { + right: 0; +} + +.commentsList { + list-style-type: none; + padding: 0; + width: 100%; + margin-top: 10px; + height: 350px; + overflow-y: auto; + position: relative; +} + +.commentsList::-webkit-scrollbar { + width: 10px; + } + +.commentsList::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.5); + border-radius: 20px; + } + +.commentsList::-webkit-scrollbar-thumb:hover { + background: #555; + } + +.commentsList li { + background: #f9f9f9; + margin-bottom: 10px; + padding: 10px; + border-radius: 10px; + width: 100%; + text-align: left; + display: flex; + flex-direction: column; + position: relative; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: background-color 0.3s, box-shadow 0.3s; +} + +.commentItem { + display: flex; + flex-direction: column; + width: 100%; + padding: 5px 0; + position: relative; +} + +.commentHeader { + display: flex; + justify-content: space-between; + width: 100%; + font-size: 0.8rem; + color: gray; + font-family: 'PretendardR'; + margin-bottom: 5px; +} + +.commentBody { + display: flex; + align-items: center; + justify-content: space-between; +} + +.commentText { + flex: 1; + padding-right: 2rem; +} + +.editCommentContainer { + display: flex; + align-items: center; +} + +.commentActions { + display: flex; + align-items: center; + font-family: 'PretendardR'; +} + +.optionsDropdown { + position: absolute; + top: 20px; + right: 0; + background: rgba(255, 255, 255, 0.95); + border: 1px solid #ddd; + border-radius: 5px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + z-index: 10; + width: 100px; + white-space: nowrap; + overflow: hidden; + align-items: center; + text-align: center; +} + +.optionsDropdown button { + display: block; + width: 100%; + padding: 10px 15px; + border: none; + background: none; + cursor: pointer; + text-align: left; + font-size: 14px; + color: #333; + transition: background-color 0.3s ease, color 0.3s ease; + text-align: center; +} + +.optionsDropdown button:hover { + background: #9E69FA; + color: #000; +} + +.editCommentInput { + flex: 1; + padding: 5px; + border-radius: 3px; +} + +.ellipsisButton, .deleteButton, .saveButton { + background: none; + border: none; + cursor: pointer; + margin-left: 0px; +} + +.ellipsisButton { + font-size: 1rem; +} + +.saveButton { + color: green; + margin-left: 5px; +} + +.deleteButton { + color: red; +} + +.FeedListcommentForm { + display: flex; + width: 100%; + position: sticky; + top: 0; + padding-bottom: 1rem; + z-index: 1; +} + +.FeedListCommentInput { + flex: 1; + width: 80%; + padding: 10px; + border: 1px solid #ccc; + border-radius: 20px; + font-size: 16px; + color: #333; + background-color: #f9f9f9; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: border-color 0.3s, box-shadow 0.3s; + font-family: 'PretendardR'; +} + +.FeedListCommentInput:focus { + border-color: #007BFF; + box-shadow: 0 2px 8px rgba(0, 123, 255, 0.2); + outline: none; +} + +.commentButton { + padding: 1% 5%; + border: none; + background-color: #9E69FA; + color: white; + border-radius: 20px; + cursor: pointer; + margin-left: 3%; + font-size: 1rem; + font-family: 'PretendardSB'; + transition: background-color 0.3s, transform 0.2s; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.commentButton:hover { + background-color: #804BDC; + transform: scale(1.05); +} + +.commentButton:active { + background-color: #804BDC; + transform: scale(0.98); +} + +.FeedListPhotoHeader { + display: flex; + justify-content: space-between; + width: 100%; + padding: 10px; +} + +.FeedListPhotoauthor { + font-size: 0.8rem; + color: gray; + font-family: 'PretendardR'; +} + +.FeedListPhototime { + font-size: 0.8rem; + color: gray; + font-family: 'PretendardR'; +} + +.FeedListPhotolikeSection { + display: flex; + align-items: center; + justify-content: center; + text-align: center; + margin-top: 3%; + width: 40%; +} + +.FeedListPhotolikeButton { + font-size: 1.5rem; + color: red; + cursor: pointer; + margin-right: 5%; +} + +.FeedListMyContentWrapper { + position: relative; + margin-left: 7%; +} + +.FeedListMyContent { + cursor: pointer; + font-size: 1.3rem; + color: #333; +} + +.FeedListOptionsDropdown { + position: absolute; + top: 1.5rem; + right: 0; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 5px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + z-index: 1000; + width: 80px; + text-align: center; +} + +.FeedListOptionsDropdown button { + width: 100%; + padding: 10px 0; + background: none; + border: none; + cursor: pointer; + font-size: 14px; + color: #333; + transition: background-color 0.3s ease; +} + +.FeedListOptionsDropdown button:hover { + background-color: #9E69FA; + color: #000; +} + +.FeedEditTextarea { + width: 70%; + height: 10%; + padding: 1%; + border: 1px solid #ccc; + border-radius: 10px; + font-size: 1rem; + font-family: 'Pretendard'; + resize: none; + background-color: #f9f9f9; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: border-color 0.3s, box-shadow 0.3s; +} + +.FeedEditTextarea:focus { + border-color: #9E69FA; + box-shadow: 0 2px 8px rgba(158, 105, 250, 0.2); + outline: none; +} + +.FeedEditTextarea::placeholder { + color: #aaa; + font-style: italic; +} \ No newline at end of file diff --git a/Front-feedme/src/components/Feed/FeedList.jsx b/Front-feedme/src/components/Feed/FeedList.jsx new file mode 100644 index 0000000..3d94e83 --- /dev/null +++ b/Front-feedme/src/components/Feed/FeedList.jsx @@ -0,0 +1,267 @@ +import React, { useState, useEffect } from 'react'; +import { FaAngleLeft, FaAngleRight, FaEllipsisH, FaHeart } from 'react-icons/fa'; +import { useDispatch, useSelector } from 'react-redux'; +import { fetchFeedList, postComment, deleteComment, editComment, deleteFeed, editFeed } from '../../store/feedListSlice'; +import { useNavigate, useLocation } from 'react-router-dom'; +import { fetchUserData } from '../../store/userSlice'; +import './FeedList.css'; +import '../../assets/font/Font.css' + + +const FeedList = () => { + const dispatch = useDispatch(); + const token = useSelector((state) => state.auth.token); + const feedList = useSelector((state) => state.feedList.feeds); + const feedListStatus = useSelector((state) => state.feedList.status); + const user = useSelector((state) => state.user); + const { email } = user; + + const navigate = useNavigate(); + const location = useLocation(); + + const [currentIndex, setCurrentIndex] = useState(0); + const [newComment, setNewComment] = useState(''); + const [editingComment, setEditingComment] = useState(null); + const [editedComment, setEditedComment] = useState(''); + const [showOptions, setShowOptions] = useState(null); + const [isEditingFeed, setIsEditingFeed] = useState(false); + const [editedFeedContent, setEditedFeedContent] = useState(''); + + useEffect(() => { + if (feedListStatus === 'idle' && token) { + dispatch(fetchFeedList(token)); + } + }, [feedListStatus, dispatch, token]); + + useEffect(() => { + const searchParams = new URLSearchParams(location.search); + const index = parseInt(searchParams.get('post'), 10); + if (!isNaN(index) && index >= 0 && index < feedList.length) { + setCurrentIndex(index); + } + }, [location.search, feedList.length]); + + useEffect(() => { + if (token) { + dispatch(fetchUserData(token)); + } + }, [dispatch, token]); + + const handlePrevClick = () => { + setCurrentIndex((prevIndex) => (prevIndex === 0 ? feedList.length - 1 : prevIndex - 1)); + }; + + const handleNextClick = () => { + setCurrentIndex((prevIndex) => (prevIndex === feedList.length - 1 ? 0 : prevIndex + 1)); + }; + + const handleCommentChange = (e) => { + setNewComment(e.target.value); + }; + + const handleCommentSubmit = (e) => { + e.preventDefault(); + if (newComment.trim() !== '') { + dispatch(postComment({ + token, + feedId: feedList[currentIndex].feedId, + content: newComment + })).then(() => { + navigate(`?post=${currentIndex}`); + window.location.reload(); + }); + } + }; + + const handleEditCommentChange = (e) => { + setEditedComment(e.target.value); + }; + + const handleCommentEdit = (index) => { + setEditingComment(index); + setEditedComment(feedList[currentIndex].comments[index].comment); + setShowOptions(null); + }; + + const handleCommentSave = (index) => { + const feedComentId = feedList[currentIndex].comments[index].feedComentId; + dispatch(editComment({ + token, + feedComentId, + updatedComment: { + nickname: feedList[currentIndex].comments[index].nickname, + content: editedComment, + createdAt: new Date().toISOString(), + }, + })).then(() => { + setEditingComment(null); + setEditedComment(''); + navigate(`?post=${currentIndex}`); + window.location.reload(); + }); + }; + + + const handleCommentDelete = (index) => { + const feedComentId = feedList[currentIndex].comments[index].feedComentId; + dispatch(deleteComment({ + token, + feedComentId + })).then(() => { + setShowOptions(null); + }); + }; + + const handleShowOptions = (index) => { + setShowOptions(showOptions === index ? null : index); + }; + + const handleLikeClick = () => { + const updatedFeedList = [...feedList]; + updatedFeedList[currentIndex].likes += 1; + // setFeedList(updatedFeedList); // Redux 상태에 반영할 방법이 필요 + }; + + const handleContEdit = () => { + setIsEditingFeed(true); + setEditedFeedContent(feedList[currentIndex].caption); + }; + + const handleFeedCancel = () => { + setIsEditingFeed(false); + setEditedFeedContent(''); + setShowOptions(null); + }; + + const handleFeedSave = () => { + const feedId = feedList[currentIndex].feedId; + dispatch(editFeed({ token, feedId, content: editedFeedContent })) + .then(() => { + setIsEditingFeed(false); + setShowOptions(null); + navigate(`?post=${currentIndex}`); + window.location.reload(); + }); + }; + + const handleContDel = () => { + const feedId = feedList[currentIndex].feedId; + dispatch(deleteFeed({ token, feedId })) + .then(() => { + setCurrentIndex(0); + setShowOptions(null); + // console.log("삭제성공") + }); + }; + + // console.log(feedList) + + return ( +
+ + + {feedList.length > 0 && ( +
+
+ {feedList[currentIndex].nickname} + {new Date(feedList[currentIndex].lastCreateTime).toLocaleString()} +
+ + {isEditingFeed ? ( +