diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a589a9d1..6b962fa4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # 프로젝트 내 모든 파일의 변경사항 발생 시, 아래 사용자가 리뷰어로 기본 지정됩니다. -*.java @fingersdanny @Hju95 @RossKWSang @mooncw \ No newline at end of file +*.java @Hju95 @RossKWSang @mooncw \ No newline at end of file diff --git a/build.gradle b/build.gradle index e0f75ffd..a56135dc 100644 --- a/build.gradle +++ b/build.gradle @@ -22,74 +22,16 @@ repositories { } dependencies { - //Spring Boot - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-data-redis' - - compileOnly 'org.projectlombok:lombok' - - runtimeOnly 'com.mysql:mysql-connector-j' - - annotationProcessor 'org.projectlombok:lombok' - - //S3 - implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.3.1' - - //QueryDSL - implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' - annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" - annotationProcessor "jakarta.annotation:jakarta.annotation-api" - annotationProcessor "jakarta.persistence:jakarta.persistence-api" - - //AOP - implementation 'org.springframework.boot:spring-boot-starter-aop' - - implementation 'org.springframework.boot:spring-boot-starter-actuator' - implementation 'io.micrometer:micrometer-registry-prometheus' - - //Security - implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' - implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' - - runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' - runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' - - //flyway - implementation 'org.flywaydb:flyway-mysql' - - //websocket - implementation 'org.springframework.boot:spring-boot-starter-websocket' - - // ====> 소켓 통신의 임시 화면을 위함, 정식 화면이 생기면 삭제 할 것 !! - implementation 'org.webjars:sockjs-client:1.1.2' - implementation 'org.webjars:stomp-websocket:2.3.3-1' - - //view - implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'org.springframework.boot:spring-boot-starter-freemarker' - implementation 'org.springframework.boot:spring-boot-devtools' - implementation 'org.webjars.bower:bootstrap:4.3.1' - implementation 'org.webjars.bower:vue:2.5.16' - implementation 'org.webjars.bower:axios:0.17.1' - implementation 'com.google.code.gson:gson:2.8.0' - // <==== - - //mongodb - implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' - - //kafka - implementation 'org.springframework.kafka:spring-kafka' - - //Test - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.springframework.security:spring-security-test' - - testImplementation 'com.h2database:h2' } tasks.named('test') { useJUnitPlatform() +} + +tasks.named('bootJar') { + enabled = false +} + +tasks.named('jar') { + enabled = false } \ No newline at end of file diff --git a/core/.gitignore b/core/.gitignore deleted file mode 100644 index c2065bc2..00000000 --- a/core/.gitignore +++ /dev/null @@ -1,37 +0,0 @@ -HELP.md -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### 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/domain-kafka/build.gradle b/domain-kafka/build.gradle new file mode 100644 index 00000000..7b8004d7 --- /dev/null +++ b/domain-kafka/build.gradle @@ -0,0 +1,50 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.0' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.kernelsquare' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '21' +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + //Modules + implementation project(':core') + + //Spring Boot + implementation 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + //kafka + implementation 'org.springframework.kafka:spring-kafka' + + //Test + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} + +tasks.named('bootJar') { + enabled = false +} + +tasks.named('jar') { + enabled = true +} diff --git a/domain-kafka/gradle/wrapper/gradle-wrapper.jar b/domain-kafka/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e6441136 Binary files /dev/null and b/domain-kafka/gradle/wrapper/gradle-wrapper.jar differ diff --git a/domain-kafka/gradle/wrapper/gradle-wrapper.properties b/domain-kafka/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..b82aa23a --- /dev/null +++ b/domain-kafka/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/domain-kafka/gradlew b/domain-kafka/gradlew new file mode 100644 index 00000000..1aa94a42 --- /dev/null +++ b/domain-kafka/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/domain-kafka/gradlew.bat b/domain-kafka/gradlew.bat new file mode 100644 index 00000000..25da30db --- /dev/null +++ b/domain-kafka/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/common/config/KafkaProducerConfig.java b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/config/KafkaProducerConfig.java similarity index 76% rename from member-api/src/main/java/com/kernelsquare/memberapi/common/config/KafkaProducerConfig.java rename to domain-kafka/src/main/java/com/kernelsquare/domainkafka/config/KafkaProducerConfig.java index 6b73ef89..5b4ee748 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/common/config/KafkaProducerConfig.java +++ b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/config/KafkaProducerConfig.java @@ -1,4 +1,4 @@ -package com.kernelsquare.memberapi.common.config; +package com.kernelsquare.domainkafka.config; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringSerializer; @@ -17,18 +17,19 @@ public class KafkaProducerConfig { @Value("${kafka.url}") private String url; + @Bean - public ProducerFactory producerFactory() { + public ProducerFactory producerFactory() { Map config = new HashMap<>(); config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, url); config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); - config.put(JsonSerializer.TYPE_MAPPINGS, "ChatMessageRequest:com.kernelsquare.memberapi.domain.coffeechat.dto.ChatMessageRequest"); + config.put(JsonSerializer.TYPE_MAPPINGS, "ChatMessage:com.kernelsquare.domainkafka.domain.chatting.entity.ChatMessage, Alert:com.kernelsquare.domainkafka.domain.alert.entity.Alert"); return new DefaultKafkaProducerFactory<>(config); } @Bean - public KafkaTemplate kafkaTemplate() { + public KafkaTemplate kafkaTemplate() { return new KafkaTemplate<>(producerFactory()); } } diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/common/config/KafkaTopicConfig.java b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/config/KafkaTopicConfig.java similarity index 71% rename from member-api/src/main/java/com/kernelsquare/memberapi/common/config/KafkaTopicConfig.java rename to domain-kafka/src/main/java/com/kernelsquare/domainkafka/config/KafkaTopicConfig.java index c722561f..47d04721 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/common/config/KafkaTopicConfig.java +++ b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/config/KafkaTopicConfig.java @@ -1,4 +1,4 @@ -package com.kernelsquare.memberapi.common.config; +package com.kernelsquare.domainkafka.config; import org.apache.kafka.clients.admin.AdminClientConfig; import org.apache.kafka.clients.admin.NewTopic; @@ -10,6 +10,9 @@ import java.util.HashMap; import java.util.Map; +import static com.kernelsquare.domainkafka.constants.KafkaConstants.ALERT_TOPIC; +import static com.kernelsquare.domainkafka.constants.KafkaConstants.CHATTING_TOPIC; + @Configuration public class KafkaTopicConfig { @Value("${kafka.url}") @@ -24,11 +27,11 @@ public KafkaAdmin kafkaAdmin() { @Bean public NewTopic chatTopic() { - return new NewTopic("chatting",1, (short)1); + return new NewTopic(CHATTING_TOPIC,1, (short)1); } @Bean public NewTopic alertTopic() { - return new NewTopic("alert",1, (short)1); + return new NewTopic(ALERT_TOPIC,1, (short)1); } } diff --git a/domain-kafka/src/main/java/com/kernelsquare/domainkafka/constants/KafkaConstants.java b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/constants/KafkaConstants.java new file mode 100644 index 00000000..de4d21c7 --- /dev/null +++ b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/constants/KafkaConstants.java @@ -0,0 +1,6 @@ +package com.kernelsquare.domainkafka.constants; + +public class KafkaConstants { + public static final String CHATTING_TOPIC = "chatting"; + public static final String ALERT_TOPIC = "alert"; +} diff --git a/domain-mongodb/src/main/java/com/kernelsquare/domainmongodb/domain/alert/entity/Alert.java b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/alert/entity/Alert.java similarity index 86% rename from domain-mongodb/src/main/java/com/kernelsquare/domainmongodb/domain/alert/entity/Alert.java rename to domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/alert/entity/Alert.java index d00d3bbc..912fd156 100644 --- a/domain-mongodb/src/main/java/com/kernelsquare/domainmongodb/domain/alert/entity/Alert.java +++ b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/alert/entity/Alert.java @@ -1,13 +1,10 @@ -package com.kernelsquare.domainmongodb.domain.alert.entity; +package com.kernelsquare.domainkafka.domain.alert.entity; import com.kernelsquare.core.util.TokenGenerator; import io.micrometer.common.util.StringUtils; import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.index.Indexed; -import org.springframework.data.mongodb.core.mapping.Document; import java.security.InvalidParameterException; import java.time.LocalDateTime; @@ -15,14 +12,11 @@ import java.util.Objects; @Getter -@Document(collection = "alert") public class Alert { private final String ALERT_PREFIX = "alt_"; - @Id private String id; - @Indexed private String recipientId; private String recipient; diff --git a/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/alert/repository/AlertSender.java b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/alert/repository/AlertSender.java new file mode 100644 index 00000000..43799bd6 --- /dev/null +++ b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/alert/repository/AlertSender.java @@ -0,0 +1,7 @@ +package com.kernelsquare.domainkafka.domain.alert.repository; + +import com.kernelsquare.domainkafka.domain.alert.entity.Alert; + +public interface AlertSender { + void send(Alert alert); +} diff --git a/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/alert/repository/AlertSenderImpl.java b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/alert/repository/AlertSenderImpl.java new file mode 100644 index 00000000..1c16ed3d --- /dev/null +++ b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/alert/repository/AlertSenderImpl.java @@ -0,0 +1,19 @@ +package com.kernelsquare.domainkafka.domain.alert.repository; + +import com.kernelsquare.domainkafka.domain.alert.entity.Alert; +import lombok.RequiredArgsConstructor; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +import static com.kernelsquare.domainkafka.constants.KafkaConstants.ALERT_TOPIC; + +@Component +@RequiredArgsConstructor +public class AlertSenderImpl implements AlertSender { + private final KafkaTemplate kafkaTemplate; + + @Override + public void send(Alert alert) { + kafkaTemplate.send(ALERT_TOPIC, alert); + } +} diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/domain/coffeechat/dto/ChatMessageRequest.java b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/chatting/entity/ChatMessage.java similarity index 80% rename from member-api/src/main/java/com/kernelsquare/memberapi/domain/coffeechat/dto/ChatMessageRequest.java rename to domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/chatting/entity/ChatMessage.java index 1e697aa7..b68536e1 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/domain/coffeechat/dto/ChatMessageRequest.java +++ b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/chatting/entity/ChatMessage.java @@ -1,4 +1,4 @@ -package com.kernelsquare.memberapi.domain.coffeechat.dto; +package com.kernelsquare.domainkafka.domain.chatting.entity; import com.kernelsquare.core.type.MessageType; import lombok.AllArgsConstructor; @@ -13,8 +13,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class ChatMessageRequest { - +public class ChatMessage { private MessageType type; private String roomKey; @@ -29,7 +28,7 @@ public class ChatMessageRequest { private LocalDateTime sendTime; - private List memberList; + private List memberList; public void setMessage(String message) { this.message = message; diff --git a/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/chatting/repository/ChatMessageSender.java b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/chatting/repository/ChatMessageSender.java new file mode 100644 index 00000000..d7deb1ab --- /dev/null +++ b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/chatting/repository/ChatMessageSender.java @@ -0,0 +1,7 @@ +package com.kernelsquare.domainkafka.domain.chatting.repository; + +import com.kernelsquare.domainkafka.domain.chatting.entity.ChatMessage; + +public interface ChatMessageSender { + void send(ChatMessage chatMessage); +} diff --git a/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/chatting/repository/ChatMessageSenderImpl.java b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/chatting/repository/ChatMessageSenderImpl.java new file mode 100644 index 00000000..ed221337 --- /dev/null +++ b/domain-kafka/src/main/java/com/kernelsquare/domainkafka/domain/chatting/repository/ChatMessageSenderImpl.java @@ -0,0 +1,19 @@ +package com.kernelsquare.domainkafka.domain.chatting.repository; + +import com.kernelsquare.domainkafka.domain.chatting.entity.ChatMessage; +import lombok.RequiredArgsConstructor; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +import static com.kernelsquare.domainkafka.constants.KafkaConstants.CHATTING_TOPIC; + +@Component +@RequiredArgsConstructor +public class ChatMessageSenderImpl implements ChatMessageSender { + private final KafkaTemplate kafkaTemplate; + + @Override + public void send(ChatMessage chatMessage) { + kafkaTemplate.send(CHATTING_TOPIC, chatMessage); + } +} diff --git a/domain-kafka/src/test/java/com/kernelsquare/domainkafka/DomainKafkaApplicationTests.java b/domain-kafka/src/test/java/com/kernelsquare/domainkafka/DomainKafkaApplicationTests.java new file mode 100644 index 00000000..2519e1d9 --- /dev/null +++ b/domain-kafka/src/test/java/com/kernelsquare/domainkafka/DomainKafkaApplicationTests.java @@ -0,0 +1,15 @@ +package com.kernelsquare.domainkafka; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@Disabled +@SpringBootTest +class DomainKafkaApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/domain-mongodb/.gitignore b/domain-mongodb/.gitignore deleted file mode 100644 index c2065bc2..00000000 --- a/domain-mongodb/.gitignore +++ /dev/null @@ -1,37 +0,0 @@ -HELP.md -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### 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/domain-mongodb/src/test/java/com/kernelsquare/domainmongodb/domain/DomainMongoApplicationTests.java b/domain-mongodb/src/test/java/com/kernelsquare/domainmongodb/DomainMongoApplicationTests.java similarity index 61% rename from domain-mongodb/src/test/java/com/kernelsquare/domainmongodb/domain/DomainMongoApplicationTests.java rename to domain-mongodb/src/test/java/com/kernelsquare/domainmongodb/DomainMongoApplicationTests.java index dd2cc1a4..1d3cfb50 100644 --- a/domain-mongodb/src/test/java/com/kernelsquare/domainmongodb/domain/DomainMongoApplicationTests.java +++ b/domain-mongodb/src/test/java/com/kernelsquare/domainmongodb/DomainMongoApplicationTests.java @@ -1,9 +1,7 @@ -package com.kernelsquare.domainmongodb.domain; +package com.kernelsquare.domainmongodb; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DomainMongoApplicationTests { - public void contextLoads() { - } } diff --git a/domain-mysql/src/test/java/com/kernelsquare/domainmysql/domain/DomainMysqlApplicationTests.java b/domain-mysql/src/test/java/com/kernelsquare/domainmysql/DomainMysqlApplicationTests.java similarity index 64% rename from domain-mysql/src/test/java/com/kernelsquare/domainmysql/domain/DomainMysqlApplicationTests.java rename to domain-mysql/src/test/java/com/kernelsquare/domainmysql/DomainMysqlApplicationTests.java index 62ae8fb5..23f53169 100644 --- a/domain-mysql/src/test/java/com/kernelsquare/domainmysql/domain/DomainMysqlApplicationTests.java +++ b/domain-mysql/src/test/java/com/kernelsquare/domainmysql/DomainMysqlApplicationTests.java @@ -1,9 +1,7 @@ -package com.kernelsquare.domainmysql.domain; +package com.kernelsquare.domainmysql; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DomainMysqlApplicationTests { - public void contextLoads() { - } } diff --git a/domain-redis/build.gradle b/domain-redis/build.gradle new file mode 100644 index 00000000..74f8e62e --- /dev/null +++ b/domain-redis/build.gradle @@ -0,0 +1,47 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.0' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.kernelsquare' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '21' +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + //Spring Boot + implementation 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + //Redis + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + + //Test + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} + +tasks.named('bootJar') { + enabled = false +} + +tasks.named('jar') { + enabled = true +} diff --git a/domain-redis/gradle/wrapper/gradle-wrapper.jar b/domain-redis/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e6441136 Binary files /dev/null and b/domain-redis/gradle/wrapper/gradle-wrapper.jar differ diff --git a/domain-redis/gradle/wrapper/gradle-wrapper.properties b/domain-redis/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..b82aa23a --- /dev/null +++ b/domain-redis/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/domain-redis/gradlew b/domain-redis/gradlew new file mode 100644 index 00000000..1aa94a42 --- /dev/null +++ b/domain-redis/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/domain-redis/gradlew.bat b/domain-redis/gradlew.bat new file mode 100644 index 00000000..25da30db --- /dev/null +++ b/domain-redis/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/domain-redis/src/main/java/com/kernelsquare/domainredis/config/RedisConfig.java b/domain-redis/src/main/java/com/kernelsquare/domainredis/config/RedisConfig.java new file mode 100644 index 00000000..0f9b84a0 --- /dev/null +++ b/domain-redis/src/main/java/com/kernelsquare/domainredis/config/RedisConfig.java @@ -0,0 +1,43 @@ +package com.kernelsquare.domainredis.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.beans.factory.annotation.Value; +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.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(host, port); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(jsonRedisSerializer()); + return redisTemplate; + } + + @Bean + GenericJackson2JsonRedisSerializer jsonRedisSerializer() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + return new GenericJackson2JsonRedisSerializer(objectMapper); + } +} diff --git a/domain-redis/src/main/java/com/kernelsquare/domainredis/constants/RedisConstans.java b/domain-redis/src/main/java/com/kernelsquare/domainredis/constants/RedisConstans.java new file mode 100644 index 00000000..76130a57 --- /dev/null +++ b/domain-redis/src/main/java/com/kernelsquare/domainredis/constants/RedisConstans.java @@ -0,0 +1,6 @@ +package com.kernelsquare.domainredis.constants; + +public class RedisConstans { + public static final String REFRESHTOKEN_PREFIX = "rft_"; + public static final Long RefreshTokenTTL = 60 * 60 * 24 * 14L; //2주 +} diff --git a/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/entity/RefreshToken.java b/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/entity/RefreshToken.java new file mode 100644 index 00000000..cce7bcdb --- /dev/null +++ b/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/entity/RefreshToken.java @@ -0,0 +1,27 @@ +package com.kernelsquare.domainredis.domain.refreshtoken.entity; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@NoArgsConstructor +public class RefreshToken { + private String memberId; + + private String refreshToken; + + private LocalDateTime createdDate; + + private LocalDateTime expirationDate; + + @Builder + public RefreshToken(String memberId, String refreshToken, LocalDateTime createdDate, LocalDateTime expirationDate) { + this.memberId = memberId; + this.refreshToken = refreshToken; + this.createdDate = createdDate; + this.expirationDate = expirationDate; + } +} diff --git a/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenReader.java b/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenReader.java new file mode 100644 index 00000000..ea2430f9 --- /dev/null +++ b/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenReader.java @@ -0,0 +1,7 @@ +package com.kernelsquare.domainredis.domain.refreshtoken.repository; + +import com.kernelsquare.domainredis.domain.refreshtoken.entity.RefreshToken; + +public interface RefreshTokenReader { + RefreshToken find(String memberId); +} diff --git a/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenReaderImpl.java b/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenReaderImpl.java new file mode 100644 index 00000000..c9a48fea --- /dev/null +++ b/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenReaderImpl.java @@ -0,0 +1,19 @@ +package com.kernelsquare.domainredis.domain.refreshtoken.repository; + +import com.kernelsquare.domainredis.domain.refreshtoken.entity.RefreshToken; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import static com.kernelsquare.domainredis.constants.RedisConstans.REFRESHTOKEN_PREFIX; + +@Component +@RequiredArgsConstructor +public class RefreshTokenReaderImpl implements RefreshTokenReader { + private final RedisTemplate redisTemplate; + + @Override + public RefreshToken find(String memberId) { + return redisTemplate.opsForValue().get(REFRESHTOKEN_PREFIX + memberId); + } +} diff --git a/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenStore.java b/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenStore.java new file mode 100644 index 00000000..4fa4ca87 --- /dev/null +++ b/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenStore.java @@ -0,0 +1,9 @@ +package com.kernelsquare.domainredis.domain.refreshtoken.repository; + +import com.kernelsquare.domainredis.domain.refreshtoken.entity.RefreshToken; + +public interface RefreshTokenStore { + void delete(RefreshToken refreshToken); + + void store(RefreshToken refreshToken); +} diff --git a/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenStoreImpl.java b/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenStoreImpl.java new file mode 100644 index 00000000..407d25a2 --- /dev/null +++ b/domain-redis/src/main/java/com/kernelsquare/domainredis/domain/refreshtoken/repository/RefreshTokenStoreImpl.java @@ -0,0 +1,27 @@ +package com.kernelsquare.domainredis.domain.refreshtoken.repository; + +import com.kernelsquare.domainredis.domain.refreshtoken.entity.RefreshToken; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +import static com.kernelsquare.domainredis.constants.RedisConstans.REFRESHTOKEN_PREFIX; +import static com.kernelsquare.domainredis.constants.RedisConstans.RefreshTokenTTL; + +@Component +@RequiredArgsConstructor +public class RefreshTokenStoreImpl implements RefreshTokenStore { + private final RedisTemplate redisTemplate; + + @Override + public void delete(RefreshToken refreshToken) { + redisTemplate.opsForValue().getOperations().delete(REFRESHTOKEN_PREFIX + refreshToken.getMemberId()); + } + + @Override + public void store(RefreshToken refreshToken) { + redisTemplate.opsForValue().set(REFRESHTOKEN_PREFIX + refreshToken.getMemberId(), refreshToken, RefreshTokenTTL, TimeUnit.SECONDS); + } +} diff --git a/domain-redis/src/test/java/com/kernelsquare/domainredis/DomainRedisApplicationTests.java b/domain-redis/src/test/java/com/kernelsquare/domainredis/DomainRedisApplicationTests.java new file mode 100644 index 00000000..fe19119d --- /dev/null +++ b/domain-redis/src/test/java/com/kernelsquare/domainredis/DomainRedisApplicationTests.java @@ -0,0 +1,15 @@ +package com.kernelsquare.domainredis; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@Disabled +@SpringBootTest +class DomainRedisApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/domain-s3/build.gradle b/domain-s3/build.gradle new file mode 100644 index 00000000..c2e7fd3b --- /dev/null +++ b/domain-s3/build.gradle @@ -0,0 +1,50 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.0' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'com.kernelsquare' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '21' +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + //Modules + implementation project(':core') + + //Spring Boot + implementation 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + //S3 + implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.3.1' + + //Test + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} + +tasks.named('bootJar') { + enabled = false +} + +tasks.named('jar') { + enabled = true +} diff --git a/domain-s3/gradle/wrapper/gradle-wrapper.jar b/domain-s3/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e6441136 Binary files /dev/null and b/domain-s3/gradle/wrapper/gradle-wrapper.jar differ diff --git a/domain-s3/gradle/wrapper/gradle-wrapper.properties b/domain-s3/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..b82aa23a --- /dev/null +++ b/domain-s3/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/domain-s3/gradlew b/domain-s3/gradlew new file mode 100644 index 00000000..1aa94a42 --- /dev/null +++ b/domain-s3/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/domain-s3/gradlew.bat b/domain-s3/gradlew.bat new file mode 100644 index 00000000..25da30db --- /dev/null +++ b/domain-s3/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/common/config/S3Config.java b/domain-s3/src/main/java/com/kernelsquare/domains3/config/S3Config.java similarity index 94% rename from member-api/src/main/java/com/kernelsquare/memberapi/common/config/S3Config.java rename to domain-s3/src/main/java/com/kernelsquare/domains3/config/S3Config.java index c22b772c..6662151b 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/common/config/S3Config.java +++ b/domain-s3/src/main/java/com/kernelsquare/domains3/config/S3Config.java @@ -1,4 +1,4 @@ -package com.kernelsquare.memberapi.common.config; +package com.kernelsquare.domains3.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; diff --git a/domain-s3/src/main/java/com/kernelsquare/domains3/domain/image/repository/ImageStore.java b/domain-s3/src/main/java/com/kernelsquare/domains3/domain/image/repository/ImageStore.java new file mode 100644 index 00000000..83e17edc --- /dev/null +++ b/domain-s3/src/main/java/com/kernelsquare/domains3/domain/image/repository/ImageStore.java @@ -0,0 +1,9 @@ +package com.kernelsquare.domains3.domain.image.repository; + +import org.springframework.web.multipart.MultipartFile; + +public interface ImageStore { + String store(String category, MultipartFile multipartFile); + + void delete(String imageUrl); +} diff --git a/domain-s3/src/main/java/com/kernelsquare/domains3/domain/image/repository/ImageStoreImpl.java b/domain-s3/src/main/java/com/kernelsquare/domains3/domain/image/repository/ImageStoreImpl.java new file mode 100644 index 00000000..4f3c3a16 --- /dev/null +++ b/domain-s3/src/main/java/com/kernelsquare/domains3/domain/image/repository/ImageStoreImpl.java @@ -0,0 +1,49 @@ +package com.kernelsquare.domains3.domain.image.repository; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.kernelsquare.core.common_response.error.code.ImageErrorCode; +import com.kernelsquare.core.common_response.error.exception.BusinessException; +import com.kernelsquare.core.util.ImageUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class ImageStoreImpl implements ImageStore { + private final AmazonS3Client amazonS3Client; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + @Override + public String store(String category, MultipartFile multipartFile) { + String filePath = ImageUtils.makeFilePath(category, multipartFile); + + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(multipartFile.getContentType()); + metadata.setContentLength(multipartFile.getSize()); + + try { + amazonS3Client.putObject(bucket, filePath, multipartFile.getInputStream(), metadata); + + String ImageUrl = amazonS3Client.getUrl(bucket, filePath).toString(); + + return ImageUrl; + } catch (IOException e) { + throw new BusinessException(ImageErrorCode.IMAGE_UPLOAD_FAILED); + } + } + + @Override + public void delete(String imageUrl) { + String keyName = ImageUtils.parseFilePath(imageUrl); + + amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, keyName)); + } +} diff --git a/domain-s3/src/test/java/com/kernelsquare/domains3/DomainS3ApplicationTests.java b/domain-s3/src/test/java/com/kernelsquare/domains3/DomainS3ApplicationTests.java new file mode 100644 index 00000000..70b978a3 --- /dev/null +++ b/domain-s3/src/test/java/com/kernelsquare/domains3/DomainS3ApplicationTests.java @@ -0,0 +1,15 @@ +package com.kernelsquare.domains3; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@Disabled +@SpringBootTest +class DomainS3ApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/member-api/.gitignore b/member-api/.gitignore deleted file mode 100644 index c2065bc2..00000000 --- a/member-api/.gitignore +++ /dev/null @@ -1,37 +0,0 @@ -HELP.md -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### 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/member-api/build.gradle b/member-api/build.gradle index 44ea633e..a02a0c22 100644 --- a/member-api/build.gradle +++ b/member-api/build.gradle @@ -43,6 +43,9 @@ dependencies { implementation project(":core") implementation project(":domain-mysql") implementation project(":domain-mongodb") + implementation project(":domain-redis") + implementation project(":domain-kafka") + implementation project(":domain-s3") //Security implementation 'org.springframework.boot:spring-boot-starter-security' @@ -58,18 +61,10 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' annotationProcessor "org.mapstruct:mapstruct-processor:1.4.2.Final" - //S3 - implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.3.1' - - //AOP - implementation 'org.springframework.boot:spring-boot-starter-aop' - + //Monitoring implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus' - //kafka - implementation 'org.springframework.kafka:spring-kafka' - //FeignClient implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.0' @@ -102,3 +97,7 @@ bootJar { into 'static/docs' } } + +tasks.named('jar') { + enabled = false +} diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/MemberApiApplication.java b/member-api/src/main/java/com/kernelsquare/memberapi/MemberApiApplication.java index 326f7676..4f844d8c 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/MemberApiApplication.java +++ b/member-api/src/main/java/com/kernelsquare/memberapi/MemberApiApplication.java @@ -7,7 +7,10 @@ "com.kernelsquare.core", "com.kernelsquare.memberapi", "com.kernelsquare.domainmysql", - "com.kernelsquare.domainmongodb"}) + "com.kernelsquare.domainmongodb", + "com.kernelsquare.domainredis", + "com.kernelsquare.domainkafka", + "com.kernelsquare.domains3"}) public class MemberApiApplication { public static void main(String[] args) { SpringApplication.run(MemberApiApplication.class, args); diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/common/config/RedisConfig.java b/member-api/src/main/java/com/kernelsquare/memberapi/common/config/RedisConfig.java deleted file mode 100644 index 6cddcce2..00000000 --- a/member-api/src/main/java/com/kernelsquare/memberapi/common/config/RedisConfig.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.kernelsquare.memberapi.common.config; - -import org.springframework.beans.factory.annotation.Value; -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.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; - -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.kernelsquare.memberapi.domain.auth.entity.RefreshToken; - -@Configuration -public class RedisConfig { - @Value("${spring.redis.serialization.class-property-type-name}") - String classPropertyTypeName; - - @Value("${spring.redis.host}") - private String host; - - @Value("${spring.redis.port}") - private int port; - - @Bean - public RedisConnectionFactory redisConnectionFactory() { - return new LettuceConnectionFactory(host, port); - } - - @Bean - public RedisTemplate redisTemplate() { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(redisConnectionFactory()); - redisTemplate.setKeySerializer(new JdkSerializationRedisSerializer()); - redisTemplate.setValueSerializer(jsonRedisSerializer()); - return redisTemplate; - } - - @Bean - GenericJackson2JsonRedisSerializer jsonRedisSerializer() { - GenericJackson2JsonRedisSerializer jsonRedisSerializer = - new GenericJackson2JsonRedisSerializer(classPropertyTypeName); - - jsonRedisSerializer.configure(objectMapper -> objectMapper - .registerModule(new JavaTimeModule())); - - return jsonRedisSerializer; - } -} diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/common/util/ChatMessageConverter.java b/member-api/src/main/java/com/kernelsquare/memberapi/common/util/ChatMessageConverter.java deleted file mode 100644 index e56b9da7..00000000 --- a/member-api/src/main/java/com/kernelsquare/memberapi/common/util/ChatMessageConverter.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.kernelsquare.memberapi.common.util; - -import com.kernelsquare.domainmongodb.domain.coffeechat.entity.MongoChatMessage; -import com.kernelsquare.domainmongodb.domain.coffeechat.entity.MongoMessageType; -import com.kernelsquare.memberapi.domain.coffeechat.dto.ChatMessageRequest; - -public class ChatMessageConverter { - public static MongoChatMessage toMongoChatMessage(ChatMessageRequest message) { - return MongoChatMessage.builder() - .roomKey(message.getRoomKey()) - .type(MongoMessageType.valueOf(String.valueOf(message.getType()))) - .senderId(message.getSenderId()) - .sender(message.getSender()) - .senderImageUrl(message.getSenderImageUrl()) - .message(message.getMessage()) - .sendTime(message.getSendTime()) - .build(); - } -} diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/AlertMessage.java b/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/AlertMessage.java index 1ce6ae4e..95839092 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/AlertMessage.java +++ b/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/AlertMessage.java @@ -1,6 +1,6 @@ package com.kernelsquare.memberapi.domain.alert.dto; -import com.kernelsquare.domainmongodb.domain.alert.entity.Alert; +import com.kernelsquare.domainkafka.domain.alert.entity.Alert; public interface AlertMessage { Alert process(); diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/CoffeeChatRequestAlertMessage.java b/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/CoffeeChatRequestAlertMessage.java index 7b0abf60..d572966c 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/CoffeeChatRequestAlertMessage.java +++ b/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/CoffeeChatRequestAlertMessage.java @@ -1,6 +1,6 @@ package com.kernelsquare.memberapi.domain.alert.dto; -import com.kernelsquare.domainmongodb.domain.alert.entity.Alert; +import com.kernelsquare.domainkafka.domain.alert.entity.Alert; import lombok.Builder; import java.util.HashMap; diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/QuestionReplyAlertMessage.java b/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/QuestionReplyAlertMessage.java index b91bed46..25abd378 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/QuestionReplyAlertMessage.java +++ b/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/QuestionReplyAlertMessage.java @@ -1,6 +1,6 @@ package com.kernelsquare.memberapi.domain.alert.dto; -import com.kernelsquare.domainmongodb.domain.alert.entity.Alert; +import com.kernelsquare.domainkafka.domain.alert.entity.Alert; import lombok.Builder; import java.util.HashMap; diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/RankAnswerAlertMessage.java b/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/RankAnswerAlertMessage.java index 0dcf69d4..b357bae9 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/RankAnswerAlertMessage.java +++ b/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/dto/RankAnswerAlertMessage.java @@ -1,7 +1,7 @@ package com.kernelsquare.memberapi.domain.alert.dto; import com.kernelsquare.core.constants.SystemConstants; -import com.kernelsquare.domainmongodb.domain.alert.entity.Alert; +import com.kernelsquare.domainkafka.domain.alert.entity.Alert; import lombok.Builder; import java.util.HashMap; diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/service/AlertServiceImpl.java b/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/service/AlertServiceImpl.java index 1b970cd4..3fa31746 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/service/AlertServiceImpl.java +++ b/member-api/src/main/java/com/kernelsquare/memberapi/domain/alert/service/AlertServiceImpl.java @@ -1,22 +1,22 @@ package com.kernelsquare.memberapi.domain.alert.service; -import com.kernelsquare.domainmongodb.domain.alert.entity.Alert; +import com.kernelsquare.domainkafka.domain.alert.entity.Alert; +import com.kernelsquare.domainkafka.domain.alert.repository.AlertSender; import com.kernelsquare.memberapi.domain.alert.dto.AlertMessage; import lombok.RequiredArgsConstructor; -import org.springframework.kafka.core.KafkaTemplate; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class AlertServiceImpl implements AlertService { - private final KafkaTemplate kafkaTemplate; + private final AlertSender alertSender; @Async @Override public void sendToBroker(AlertMessage alertMessage) { Alert alert = alertMessage.process(); - kafkaTemplate.send("alert", alert); + alertSender.send(alert); } } diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/domain/auth/entity/RefreshToken.java b/member-api/src/main/java/com/kernelsquare/memberapi/domain/auth/entity/RefreshToken.java deleted file mode 100644 index 63745c4a..00000000 --- a/member-api/src/main/java/com/kernelsquare/memberapi/domain/auth/entity/RefreshToken.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.kernelsquare.memberapi.domain.auth.entity; - -import java.time.LocalDateTime; - -import org.springframework.data.redis.core.RedisHash; - -import jakarta.persistence.Id; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@RedisHash(value = "refreshToken", timeToLive = 60) -public class RefreshToken { - @Id - private Long memberId; - - private String refreshToken; - - private LocalDateTime createdDate; - - private LocalDateTime expirationDate; - - @Builder - public RefreshToken(Long memberId, String refreshToken, LocalDateTime createdDate, LocalDateTime expirationDate) { - this.memberId = memberId; - this.refreshToken = refreshToken; - this.createdDate = createdDate; - this.expirationDate = expirationDate; - } -} diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/domain/auth/service/TokenProvider.java b/member-api/src/main/java/com/kernelsquare/memberapi/domain/auth/service/TokenProvider.java index 39388d23..3c53e8b2 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/domain/auth/service/TokenProvider.java +++ b/member-api/src/main/java/com/kernelsquare/memberapi/domain/auth/service/TokenProvider.java @@ -7,10 +7,12 @@ import com.kernelsquare.core.common_response.error.exception.BusinessException; import com.kernelsquare.domainmysql.domain.auth.info.AuthInfo; import com.kernelsquare.domainmysql.domain.member.info.MemberInfo; +import com.kernelsquare.domainredis.domain.refreshtoken.entity.RefreshToken; +import com.kernelsquare.domainredis.domain.refreshtoken.repository.RefreshTokenReader; +import com.kernelsquare.domainredis.domain.refreshtoken.repository.RefreshTokenStore; import com.kernelsquare.memberapi.domain.auth.dto.MemberAdapter; import com.kernelsquare.memberapi.domain.auth.dto.TokenRequest; import com.kernelsquare.memberapi.domain.auth.dto.TokenResponse; -import com.kernelsquare.memberapi.domain.auth.entity.RefreshToken; import io.jsonwebtoken.*; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.io.Encoders; @@ -18,7 +20,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -52,11 +53,12 @@ public class TokenProvider implements InitializingBean { @Value("${spring.security.jwt.refresh-token-validity-in-seconds}") private long refreshTokenValidityInSeconds; - private final RedisTemplate redisTemplate; private final MemberDetailService memberDetailService; + private final RefreshTokenReader refreshTokenReader; + private final RefreshTokenStore refreshTokenStore; @Override - public void afterPropertiesSet() throws Exception { + public void afterPropertiesSet() { byte[] keyBytes = Decoders.BASE64.decode(secret); this.key = Keys.hmacShaKeyFor(keyBytes); } @@ -64,7 +66,7 @@ public void afterPropertiesSet() throws Exception { public void logout(TokenRequest tokenRequest) { RefreshToken refreshToken = toRefreshToken( new String(Base64.getDecoder().decode(tokenRequest.refreshToken()), StandardCharsets.UTF_8)); - redisTemplate.opsForValue().getOperations().delete(refreshToken.getMemberId()); + refreshTokenStore.delete(refreshToken); } public AuthInfo.LoginInfo createToken(MemberInfo memberInfo) { @@ -105,11 +107,10 @@ private String createRefreshToken(String sub) { .refreshToken(uuid) .createdDate(LocalDateTime.now()) .expirationDate(expirationDate) - .memberId(Long.parseLong(sub)) + .memberId(sub) .build(); - redisTemplate.opsForValue().set(refreshToken.getMemberId(), refreshToken); - + refreshTokenStore.store(refreshToken); return Encoders.BASE64.encode(toJsonString(refreshToken).getBytes()); } @@ -184,13 +185,13 @@ public boolean validateAccessToken(String token) { public TokenResponse reissueToken(TokenRequest tokenRequest) { Claims claims = parseClaims(tokenRequest.accessToken()); String findIdByAccessToken = parseClaims(tokenRequest.accessToken()).getSubject(); - RefreshToken refreshToken = redisTemplate.opsForValue().get(Long.parseLong(findIdByAccessToken)); + RefreshToken refreshToken = refreshTokenReader.find(findIdByAccessToken); validateReissueToken(refreshToken, findIdByAccessToken); return TokenResponse.builder() - .accessToken(createAccessToken(String.valueOf(refreshToken.getMemberId()), claims.get("auth").toString())) - .refreshToken(createRefreshToken(String.valueOf(refreshToken.getMemberId()))) + .accessToken(createAccessToken(refreshToken.getMemberId(), claims.get("auth").toString())) + .refreshToken(tokenRequest.refreshToken()) .build(); } @@ -198,8 +199,8 @@ public TokenResponse reissueToken(TokenRequest tokenRequest) { * Reissued Token 유효성 검증을 수행 **/ private void validateReissueToken(RefreshToken refreshToken, String accessTokenId) { - if (!refreshToken.getExpirationDate().isAfter(LocalDateTime.now())) { - redisTemplate.opsForValue().getOperations().delete(refreshToken.getMemberId()); + if (refreshToken.getExpirationDate().isBefore(LocalDateTime.now())) { + refreshTokenStore.delete(refreshToken); throw new BusinessException(EXPIRED_LOGIN_INFO); } if (!accessTokenId.equals(String.valueOf(refreshToken.getMemberId()))) { diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/domain/image/service/ImageService.java b/member-api/src/main/java/com/kernelsquare/memberapi/domain/image/service/ImageService.java index 2ab86fe8..6bff5e36 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/domain/image/service/ImageService.java +++ b/member-api/src/main/java/com/kernelsquare/memberapi/domain/image/service/ImageService.java @@ -1,27 +1,16 @@ package com.kernelsquare.memberapi.domain.image.service; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.model.DeleteObjectRequest; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.kernelsquare.core.common_response.error.code.ImageErrorCode; -import com.kernelsquare.core.common_response.error.exception.BusinessException; -import com.kernelsquare.memberapi.domain.image.validation.ImageValidation; +import com.kernelsquare.domains3.domain.image.repository.ImageStore; import com.kernelsquare.memberapi.domain.image.dto.UploadImageResponse; -import com.kernelsquare.core.util.ImageUtils; +import com.kernelsquare.memberapi.domain.image.validation.ImageValidation; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; - @Service @RequiredArgsConstructor public class ImageService { - private final AmazonS3Client amazonS3Client; - - @Value("${cloud.aws.s3.bucket}") - private String bucket; + private final ImageStore imageStore; public UploadImageResponse uploadImage(String category, MultipartFile multipartFile) { ImageValidation.validateCategory(category); @@ -30,27 +19,12 @@ public UploadImageResponse uploadImage(String category, MultipartFile multipartF ImageValidation.validateFileExtension(multipartFile); - String filePath = ImageUtils.makeFilePath(category, multipartFile); + String imageUrl = imageStore.store(category, multipartFile); - ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentType(multipartFile.getContentType()); - metadata.setContentLength(multipartFile.getSize()); - - try { - amazonS3Client.putObject(bucket, filePath, multipartFile.getInputStream(), metadata); - - UploadImageResponse uploadImageResponse = UploadImageResponse.from( - amazonS3Client.getUrl(bucket, filePath).toString()); - - return uploadImageResponse; - } catch (IOException e) { - throw new BusinessException(ImageErrorCode.IMAGE_UPLOAD_FAILED); - } + return UploadImageResponse.from(imageUrl); } public void deleteImage(String imageUrl) { - String keyName = ImageUtils.parseFilePath(imageUrl); - - amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, keyName)); + imageStore.delete(imageUrl); } } diff --git a/member-api/src/main/java/com/kernelsquare/memberapi/domain/scheduler/manager/SchedulerManagerImpl.java b/member-api/src/main/java/com/kernelsquare/memberapi/domain/scheduler/manager/SchedulerManagerImpl.java index 3a73c1fd..29ec4c51 100644 --- a/member-api/src/main/java/com/kernelsquare/memberapi/domain/scheduler/manager/SchedulerManagerImpl.java +++ b/member-api/src/main/java/com/kernelsquare/memberapi/domain/scheduler/manager/SchedulerManagerImpl.java @@ -1,9 +1,10 @@ package com.kernelsquare.memberapi.domain.scheduler.manager; import com.kernelsquare.core.type.MessageType; +import com.kernelsquare.domainkafka.domain.chatting.entity.ChatMessage; +import com.kernelsquare.domainkafka.domain.chatting.repository.ChatMessageSender; import com.kernelsquare.domainmysql.domain.answer.entity.Answer; import com.kernelsquare.domainmysql.domain.answer.repository.AnswerReader; -import com.kernelsquare.domainmysql.domain.answer.repository.AnswerRepository; import com.kernelsquare.domainmysql.domain.answer.repository.AnswerStore; import com.kernelsquare.domainmysql.domain.coffeechat.entity.ChatRoom; import com.kernelsquare.domainmysql.domain.coffeechat.repository.CoffeeChatReader; @@ -14,9 +15,7 @@ import com.kernelsquare.memberapi.domain.alert.dto.AlertDto; import com.kernelsquare.memberapi.domain.alert.mapper.AlertDtoMapper; import com.kernelsquare.memberapi.domain.alert.service.AlertService; -import com.kernelsquare.memberapi.domain.coffeechat.dto.ChatMessageRequest; import lombok.RequiredArgsConstructor; -import org.springframework.kafka.core.KafkaTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -28,7 +27,7 @@ @Component @RequiredArgsConstructor public class SchedulerManagerImpl implements ScheculerManager { - private final KafkaTemplate kafkaTemplate; + private final ChatMessageSender chatMessageSender; private final CoffeeChatReader coffeeChatReader; private final QuestionReader questionReader; private final AnswerReader answerReader; @@ -45,7 +44,7 @@ public void disableChatRoom() { chatRooms.forEach(chatRoom -> { chatRoom.deactivateRoom(); - ChatMessageRequest message = ChatMessageRequest.builder() + ChatMessage message = ChatMessage.builder() .type(MessageType.EXPIRE) .roomKey(chatRoom.getRoomKey()) .sender("system") @@ -53,7 +52,7 @@ public void disableChatRoom() { .sendTime(LocalDateTime.now()) .build(); - kafkaTemplate.send("chatting", message); + chatMessageSender.send(message); }); } diff --git a/member-api/src/main/resources/application-prod1.yml b/member-api/src/main/resources/application-prod1.yml index d8f86785..8fb41a83 100644 --- a/member-api/src/main/resources/application-prod1.yml +++ b/member-api/src/main/resources/application-prod1.yml @@ -20,16 +20,14 @@ spring: username: ENC(vo+sZllavQ5dUMAS+k1SsGSJf5BrTZtXzqnaX4bN0GMTEy68fNhJQRj8j5odRvUx) password: ENC(Wy92J68MUYaK1KYa7MFDLaFId5UjF7aXXwVtm1gAzaWf6N9x62XpTfX4+7775DbS) - redis: - serialization: - class-property-type-name: RefreshToken.class - host: ENC(5eptbYrX+rj/3RJc4u9yGNtitdm9H2Wokuv5E6+SnP314pR2pa/EA6MfaPluTn2H) - port: ENC(dmzvcvQsEjm4GgMNlnmICsHBgBW1FaPIUTTD9sLsZWf8wzfQQN2VGdeOBJ0fRD80) - data: mongodb: uri: ENC(AjN7dXKqXwotJaOKS8TpDtDYfXpx37ixGFQz88TkgbbNYloxQEKJ7vK5P+FicD1DXz/4S/rOXTkAfZPhXonjP2iIcOV+MwkBpPopbBdV6mWCOy4ryRLaKpWqmVpjAM+wYxY2D2i9m3CAvsu26Z14Ww==) + redis: + host: ENC(5eptbYrX+rj/3RJc4u9yGNtitdm9H2Wokuv5E6+SnP314pR2pa/EA6MfaPluTn2H) + port: ENC(dmzvcvQsEjm4GgMNlnmICsHBgBW1FaPIUTTD9sLsZWf8wzfQQN2VGdeOBJ0fRD80) + kafka: url: ENC(un1AzYxYHfE5fZ+ihKn86ql8bmN1iz+2R3e4QHyuTJfdmlrgre0oUhJLYEghwz7G) diff --git a/member-api/src/main/resources/application-prod2.yml b/member-api/src/main/resources/application-prod2.yml index e1f63f94..9be2b63e 100644 --- a/member-api/src/main/resources/application-prod2.yml +++ b/member-api/src/main/resources/application-prod2.yml @@ -20,16 +20,14 @@ spring: username: ENC(vo+sZllavQ5dUMAS+k1SsGSJf5BrTZtXzqnaX4bN0GMTEy68fNhJQRj8j5odRvUx) password: ENC(Wy92J68MUYaK1KYa7MFDLaFId5UjF7aXXwVtm1gAzaWf6N9x62XpTfX4+7775DbS) - redis: - serialization: - class-property-type-name: RefreshToken.class - host: ENC(5eptbYrX+rj/3RJc4u9yGNtitdm9H2Wokuv5E6+SnP314pR2pa/EA6MfaPluTn2H) - port: ENC(dmzvcvQsEjm4GgMNlnmICsHBgBW1FaPIUTTD9sLsZWf8wzfQQN2VGdeOBJ0fRD80) - data: mongodb: uri: ENC(AjN7dXKqXwotJaOKS8TpDtDYfXpx37ixGFQz88TkgbbNYloxQEKJ7vK5P+FicD1DXz/4S/rOXTkAfZPhXonjP2iIcOV+MwkBpPopbBdV6mWCOy4ryRLaKpWqmVpjAM+wYxY2D2i9m3CAvsu26Z14Ww==) + redis: + host: ENC(5eptbYrX+rj/3RJc4u9yGNtitdm9H2Wokuv5E6+SnP314pR2pa/EA6MfaPluTn2H) + port: ENC(dmzvcvQsEjm4GgMNlnmICsHBgBW1FaPIUTTD9sLsZWf8wzfQQN2VGdeOBJ0fRD80) + kafka: url: ENC(un1AzYxYHfE5fZ+ihKn86ql8bmN1iz+2R3e4QHyuTJfdmlrgre0oUhJLYEghwz7G) diff --git a/member-api/src/test/java/com/kernelsquare/memberapi/domain/auth/service/TokenProviderTest.java b/member-api/src/test/java/com/kernelsquare/memberapi/domain/auth/service/TokenProviderTest.java index 74afdd92..27f2be9f 100644 --- a/member-api/src/test/java/com/kernelsquare/memberapi/domain/auth/service/TokenProviderTest.java +++ b/member-api/src/test/java/com/kernelsquare/memberapi/domain/auth/service/TokenProviderTest.java @@ -10,11 +10,13 @@ import com.kernelsquare.domainmysql.domain.member.entity.Member; import com.kernelsquare.domainmysql.domain.member.info.MemberInfo; import com.kernelsquare.domainmysql.domain.member_authority.entity.MemberAuthority; +import com.kernelsquare.domainredis.domain.refreshtoken.entity.RefreshToken; +import com.kernelsquare.domainredis.domain.refreshtoken.repository.RefreshTokenReader; +import com.kernelsquare.domainredis.domain.refreshtoken.repository.RefreshTokenStore; import com.kernelsquare.memberapi.domain.auth.dto.MemberAdapter; import com.kernelsquare.memberapi.domain.auth.dto.MemberAdaptorInstance; import com.kernelsquare.memberapi.domain.auth.dto.TokenRequest; import com.kernelsquare.memberapi.domain.auth.dto.TokenResponse; -import com.kernelsquare.memberapi.domain.auth.entity.RefreshToken; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import org.junit.jupiter.api.BeforeEach; @@ -23,14 +25,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.RedisOperations; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.ValueOperations; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.test.util.ReflectionTestUtils; import java.util.List; @@ -43,11 +38,12 @@ public class TokenProviderTest { @InjectMocks private TokenProvider tokenProvider; - @Spy - private RedisTemplate redisTemplate = spy(RedisTemplate.class); - @Mock private MemberDetailService memberDetailService; + @Mock + private RefreshTokenReader refreshTokenReader; + @Mock + private RefreshTokenStore refreshTokenStore; private String secret = "dGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRfdGVzdF9zZWNyZXRf"; @@ -60,20 +56,6 @@ public void setUp() { objectMapper.registerModule(new JavaTimeModule()); objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - - String classPropertyTypeName = "RefreshToken.class"; - GenericJackson2JsonRedisSerializer jsonRedisSerializer = - new GenericJackson2JsonRedisSerializer(classPropertyTypeName); - jsonRedisSerializer.configure(objectMapper -> objectMapper - .registerModule(new JavaTimeModule())); - - LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(); - lettuceConnectionFactory.afterPropertiesSet(); - - redisTemplate.setConnectionFactory(lettuceConnectionFactory); - redisTemplate.setKeySerializer(new JdkSerializationRedisSerializer()); - redisTemplate.setValueSerializer(jsonRedisSerializer); - redisTemplate.afterPropertiesSet(); } @Test @@ -116,7 +98,7 @@ void testCreateToken() throws Exception { .decode(loginInfo.refreshToken()), RefreshToken.class); //then - assertThat(createdRefreshToken.getMemberId()).isEqualTo(member.getId()); + assertThat(createdRefreshToken.getMemberId()).isEqualTo(member.getId().toString()); //verify verify(memberDetailService, only()).loadUserByUsername(anyString()); @@ -127,7 +109,6 @@ void testCreateToken() throws Exception { @DisplayName("로그 아웃 테스트") void testLogout() throws Exception { //given - Long testMemberId = 1L; String accessToken = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyIiwiYXV0aCI6IlJPTEVfVVNFUiIsImV4cCI6MTkwMDAwMDAwMH0.eHSaEoaFl9Rb7R6YxwyKiACOObHN0XjiNO7T7i1KpkiqVbgz9hQr5EPq5DuRliA_UlsBeIvfU8UHPG7xhwdcRg"; String refreshTokenString = "eyJtZW1iZXJJZCI6MiwicmVmcmVzaFRva2VuIjoiMjYzOGUxYjQ3MmI2NDRkNTk4YzY1NGNlZWFlN2FhOTAiLCJjcmVhdGVkRGF0ZSI6IjIwMjQtMDEtMTBUMjE6MDA6MzIuNDYxOCIsImV4cGlyYXRpb25EYXRlIjoiMjAyNC0wMS0yNFQyMTowMDozMi40NjE3NiJ9"; @@ -136,33 +117,15 @@ void testLogout() throws Exception { .accessToken(accessToken) .build(); - ValueOperations longRefreshTokenValueOperations = mock(ValueOperations.class); - - RedisOperations operations = mock(RedisOperations.class); - - doReturn(longRefreshTokenValueOperations) - .when(redisTemplate) - .opsForValue(); - - doReturn(operations) - .when(longRefreshTokenValueOperations) - .getOperations(); - - doReturn(Boolean.TRUE) - .when(operations) - .delete(anyLong()); + doNothing() + .when(refreshTokenStore) + .delete(any(RefreshToken.class)); //when tokenProvider.logout(tokenRequest); - Boolean delete = redisTemplate.opsForValue().getOperations().delete(testMemberId); - - //then - assertThat(delete).isTrue(); //verify - verify(redisTemplate, times(2)).opsForValue(); - verify(redisTemplate.opsForValue(), times(2)).getOperations(); - verify(redisTemplate.opsForValue().getOperations(), times(2)).delete(anyLong()); + verify(refreshTokenStore, times(1)).delete(any(RefreshToken.class)); } /** @@ -186,26 +149,20 @@ void testReissueToken() throws Exception { RefreshToken refreshToken = objectMapper.readValue(Decoders.BASE64 .decode(refreshTokenString), RefreshToken.class); - ValueOperations longRefreshTokenValueOperations = mock(ValueOperations.class); - - doReturn(longRefreshTokenValueOperations) - .when(redisTemplate) - .opsForValue(); + System.out.println("refreshToken를 함 까보자 : " + refreshToken.getRefreshToken()); doReturn(refreshToken) - .when(longRefreshTokenValueOperations) - .get(anyLong()); + .when(refreshTokenReader) + .find(anyString()); //when TokenResponse tokenResponse = tokenProvider.reissueToken(tokenRequest); - RefreshToken createdRefreshToken = objectMapper.readValue(Decoders.BASE64.decode(tokenResponse.refreshToken()), - RefreshToken.class); //then - assertThat(createdRefreshToken.getMemberId()).isEqualTo(refreshToken.getMemberId()); + assertThat(tokenResponse.accessToken()).isNotEqualTo(accessToken); + assertThat(tokenResponse.refreshToken()).isEqualTo(refreshTokenString); //verify - verify(redisTemplate, times(2)).opsForValue(); - verify(redisTemplate.opsForValue(), times(1)).get(anyLong()); + verify(refreshTokenReader, times(1)).find(anyString()); } } diff --git a/member-api/src/test/java/com/kernelsquare/memberapi/domain/image/service/ImageServiceTest.java b/member-api/src/test/java/com/kernelsquare/memberapi/domain/image/service/ImageServiceTest.java index 6df0035f..5f9450f4 100644 --- a/member-api/src/test/java/com/kernelsquare/memberapi/domain/image/service/ImageServiceTest.java +++ b/member-api/src/test/java/com/kernelsquare/memberapi/domain/image/service/ImageServiceTest.java @@ -1,13 +1,7 @@ package com.kernelsquare.memberapi.domain.image.service; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.*; - -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; - +import com.kernelsquare.domains3.domain.image.repository.ImageStore; +import com.kernelsquare.memberapi.domain.image.dto.UploadImageResponse; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -16,12 +10,12 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.model.DeleteObjectRequest; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.PutObjectResult; -import com.kernelsquare.memberapi.domain.image.dto.UploadImageResponse; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.*; @DisplayName("이미지 서비스 통합 테스트") @ExtendWith(MockitoExtension.class) @@ -29,11 +23,11 @@ class ImageServiceTest { @InjectMocks private ImageService imageService; @Mock - private AmazonS3Client amazonS3Client; + private ImageStore imageStore; @Test @DisplayName("이미지 업로드 테스트") - void testUploadImage() throws MalformedURLException { + void testUploadImage() { // given String category = "question"; @@ -44,25 +38,20 @@ void testUploadImage() throws MalformedURLException { "Hello, World!".getBytes() ); - PutObjectResult mockPutObjectResult = new PutObjectResult(); - - URL mockUrl = new URL("https://test-bucket.s3.amazonaws.com/hello.png"); + String imageUrl = "https://test-bucket.s3.amazonaws.com/" + category + "/" + file.getOriginalFilename(); - given( - amazonS3Client.putObject(any(), anyString(), any(InputStream.class), any(ObjectMetadata.class))).willReturn( - mockPutObjectResult); - given(amazonS3Client.getUrl(any(), anyString())).willReturn(mockUrl); + doReturn(imageUrl) + .when(imageStore) + .store(category, file); // when UploadImageResponse uploadImageResponse = imageService.uploadImage(category, file); // then - assertThat(uploadImageResponse.imageUrl()).isEqualTo(mockUrl.toString()); + assertThat(uploadImageResponse.imageUrl()).isEqualTo(imageUrl); //verify - verify(amazonS3Client, times(1)).putObject(any(), anyString(), any(InputStream.class), - any(ObjectMetadata.class)); - verify(amazonS3Client, times(1)).getUrl(any(), anyString()); + verify(imageStore, times(1)).store(anyString(), any(MultipartFile.class)); } @Test @@ -75,6 +64,6 @@ void deleteImage_ValidImageUrl_DeletesImage() { imageService.deleteImage(imageUrl); // then - verify(amazonS3Client, times(1)).deleteObject(any(DeleteObjectRequest.class)); + verify(imageStore, times(1)).delete(anyString()); } } \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh deleted file mode 100644 index dd2df6b6..00000000 --- a/scripts/deploy.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -echo "> 현재 시간: $(date)" >> /home/ks_project/KernelSquare/deploy.log - -REPOSITORY=/home/ks_project/KernelSquare -cd $REPOSITORY - -APP_NAME=KernelSquare -JAR_NAME=$(ls $REPOSITORY/build/libs/ | grep 'SNAPSHOT.jar' | tail -n 1) -JAR_PATH=$REPOSITORY/build/libs/$JAR_NAME - -echo "> build 파일명: $JAR_NAME" >> /home/ks_project/KernelSquare/deploy.log -echo "> build 파일 복사" >> /home/ks_project/KernelSquare/deploy.log - -echo "> 현재 실행중인 애플리케이션 pid 확인" >> /home/ks_project/KernelSquare/deploy.log -CURRENT_PID=$(pgrep -f $APP_NAME) - -if [ -z $CURRENT_PID ] -then - echo "> 종료할것 없음." -else - echo "> kill -9 $CURRENT_PID" - kill -15 $CURRENT_PID - sleep 5 -fi - -echo "> $JAR_PATH 배포" >> /home/ks_project/KernelSquare/deploy.log -echo "> $JAR_PATH 배포" -sudo nohup java -jar $JAR_PATH >> /home/ks_project/KernelSquare/tomcat.log 2> /home/ks_project/KernelSquare/deploy_err.log & \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index aaee4250..b3a88901 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,4 +3,6 @@ include 'member-api' include 'core' include 'domain-mongodb' include 'domain-mysql' - +include 'domain-redis' +include 'domain-kafka' +include 'domain-s3'