├── .gitignore ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── java │ └── mpersand │ │ └── Gmuwiki │ │ ├── GMuwikiApplication.java │ │ ├── domain │ │ ├── auth │ │ │ ├── entity │ │ │ │ └── RefreshToken.java │ │ │ ├── exception │ │ │ │ └── RoleNotExistException.java │ │ │ ├── presentation │ │ │ │ ├── AuthController.java │ │ │ │ └── data │ │ │ │ │ ├── dto │ │ │ │ │ └── LoginDto.java │ │ │ │ │ ├── request │ │ │ │ │ └── LoginRequest.java │ │ │ │ │ └── response │ │ │ │ │ ├── LoginResponse.java │ │ │ │ │ └── NewTokenResponse.java │ │ │ ├── repository │ │ │ │ └── RefreshTokenRepository.java │ │ │ ├── sevice │ │ │ │ ├── TokenReissueService.java │ │ │ │ ├── UserLoginService.java │ │ │ │ └── UserLogoutService.java │ │ │ └── util │ │ │ │ ├── AuthConverter.java │ │ │ │ └── AuthUtil.java │ │ ├── board │ │ │ ├── entity │ │ │ │ ├── Board.java │ │ │ │ └── BoardRecord.java │ │ │ ├── enums │ │ │ │ ├── BoardDetailType.java │ │ │ │ └── BoardType.java │ │ │ ├── exception │ │ │ │ ├── BoardAuthorMismatchException.java │ │ │ │ ├── BoardNotChangeException.java │ │ │ │ ├── BoardNotFoundException.java │ │ │ │ ├── BoardRecordNotFoundException.java │ │ │ │ └── ExistTitleException.java │ │ │ ├── presentation │ │ │ │ ├── BoardController.java │ │ │ │ └── dto │ │ │ │ │ ├── request │ │ │ │ │ ├── CreateBoardRequest.java │ │ │ │ │ ├── EditBoardRequest.java │ │ │ │ │ └── SearchBoardTitleRequest.java │ │ │ │ │ └── response │ │ │ │ │ ├── BoardRecordResponse.java │ │ │ │ │ ├── BoardResponse.java │ │ │ │ │ ├── DetailBoardResponse.java │ │ │ │ │ ├── ListBoardRecordResponse.java │ │ │ │ │ ├── ListBoardResponse.java │ │ │ │ │ ├── ListResentBoardResponse.java │ │ │ │ │ ├── ListSearchBoardResponse.java │ │ │ │ │ ├── ResentBoardResponse.java │ │ │ │ │ └── SearchBoardResponse.java │ │ │ ├── repository │ │ │ │ ├── BoardRecordRepository.java │ │ │ │ ├── BoardRepository.java │ │ │ │ ├── BoardRepositoryCustom.java │ │ │ │ └── impl │ │ │ │ │ └── BoardRepositoryCustomImpl.java │ │ │ └── service │ │ │ │ ├── CreateBoardService.java │ │ │ │ ├── DeleteBoardRecordService.java │ │ │ │ ├── DeleteBoardService.java │ │ │ │ ├── EditBoardService.java │ │ │ │ ├── GetBoardDetailService.java │ │ │ │ ├── GetBoardRecordDetailService.java │ │ │ │ ├── ListBoardRecordService.java │ │ │ │ ├── ListBoardService.java │ │ │ │ ├── ListRecentEditBoardService.java │ │ │ │ └── ListSearchBoardTitleService.java │ │ ├── file │ │ │ ├── exception │ │ │ │ ├── FileUploadFailedException.java │ │ │ │ ├── InvalidFormatFileException.java │ │ │ │ └── NotAllowedFileException.java │ │ │ ├── presentation │ │ │ │ ├── FileController.java │ │ │ │ └── dto │ │ │ │ │ └── response │ │ │ │ │ └── FileUploadResponse.java │ │ │ └── service │ │ │ │ └── FileUploadService.java │ │ ├── inquiry │ │ │ ├── entity │ │ │ │ └── Inquiry.java │ │ │ ├── enums │ │ │ │ └── InquiryType.java │ │ │ ├── exception │ │ │ │ ├── EmailSendFailedException.java │ │ │ │ └── InquiryNotFoundException.java │ │ │ ├── presentation │ │ │ │ ├── InquiryController.java │ │ │ │ └── dto │ │ │ │ │ ├── request │ │ │ │ │ ├── InquirySendRequest.java │ │ │ │ │ └── InquiryWriteRequest.java │ │ │ │ │ └── response │ │ │ │ │ ├── DetailInquiryResponse.java │ │ │ │ │ ├── InquiryResponse.java │ │ │ │ │ └── ListInquiryResponse.java │ │ │ ├── repository │ │ │ │ └── InquiryRepository.java │ │ │ └── service │ │ │ │ ├── CreateInquiryService.java │ │ │ │ ├── GetInquiryDetailService.java │ │ │ │ ├── InquiryApproveService.java │ │ │ │ ├── InquiryRefusalService.java │ │ │ │ └── ListInquiryService.java │ │ ├── notice │ │ │ ├── entity │ │ │ │ └── Notice.java │ │ │ ├── exception │ │ │ │ └── NoticeNotFoundException.java │ │ │ ├── presentation │ │ │ │ ├── NoticeController.java │ │ │ │ └── dto │ │ │ │ │ ├── request │ │ │ │ │ ├── CreateNoticeRequest.java │ │ │ │ │ └── EditNoticeRequest.java │ │ │ │ │ └── response │ │ │ │ │ ├── NoticeDetailResponse.java │ │ │ │ │ ├── NoticeListResponse.java │ │ │ │ │ └── NoticeResponse.java │ │ │ ├── repository │ │ │ │ └── NoticeRepository.java │ │ │ └── service │ │ │ │ ├── CreateNoticeService.java │ │ │ │ ├── DeleteNoticeService.java │ │ │ │ ├── EditNoticeService.java │ │ │ │ ├── GetNoticeDetailService.java │ │ │ │ └── ListNoticeService.java │ │ └── user │ │ │ ├── entity │ │ │ └── User.java │ │ │ ├── enums │ │ │ └── Role.java │ │ │ ├── exception │ │ │ └── UserNotFoundException.java │ │ │ ├── presentation │ │ │ ├── RoleController.java │ │ │ └── dto │ │ │ │ └── request │ │ │ │ └── RoleChangeRequest.java │ │ │ ├── repository │ │ │ └── UserRepository.java │ │ │ └── service │ │ │ ├── AdminRoleChangeService.java │ │ │ └── UserRoleChangeService.java │ │ └── global │ │ ├── annotation │ │ ├── ReadOnlyService.java │ │ ├── RestRequestService.java │ │ └── RollbackService.java │ │ ├── config │ │ ├── querydsl │ │ │ └── QuerydslConfig.java │ │ └── s3 │ │ │ └── S3Config.java │ │ ├── error │ │ ├── ErrorCode.java │ │ ├── ErrorMessage.java │ │ ├── GmuwikiException.java │ │ └── handler │ │ │ └── GlobalExceptionHandler.java │ │ ├── filter │ │ ├── ExceptionFilter.java │ │ └── JwtRequestFilter.java │ │ ├── gauth │ │ ├── GAuthConfig.java │ │ └── properties │ │ │ └── GAuthProperties.java │ │ ├── logger │ │ ├── LoggingScheduler.java │ │ └── filter │ │ │ └── LogRequestFilter.java │ │ ├── redis │ │ ├── RedisConfig.java │ │ └── properties │ │ │ └── RedisProperties.java │ │ ├── security │ │ ├── auth │ │ │ ├── AuthDetails.java │ │ │ └── AuthDetailsService.java │ │ ├── config │ │ │ └── SecurityConfig.java │ │ ├── exception │ │ │ ├── RefreshTokenExpirationException.java │ │ │ ├── TokenExpirationException.java │ │ │ └── TokenNotValidException.java │ │ ├── handler │ │ │ ├── CustomAccessDeniedHandler.java │ │ │ └── CustomAuthenticationEntryPointHandler.java │ │ └── jwt │ │ │ ├── TokenProvider.java │ │ │ └── properties │ │ │ ├── JwtProperties.java │ │ │ └── TokenTimeProperties.java │ │ ├── util │ │ ├── BoardRecordUtil.java │ │ ├── BoardUtil.java │ │ ├── NoticeUtil.java │ │ └── UserUtil.java │ │ ├── web │ │ └── WebConfig.java │ │ └── webhook │ │ ├── config │ │ └── WebhookConfig.java │ │ └── util │ │ └── DiscordUtil.java └── resources │ ├── application.yml │ └── logback-spring.xml └── test └── java └── mpersand └── Gmuwiki └── GMuwikiApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | 39 | ### log ### 40 | src/main/resources/logs -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gmu-Wiki/Gmu-Wiki-Back/e8cd1a8247cef522c0a49e5d035a88be1e77ed8f/README.md -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | //querydsl 2 | buildscript { 3 | ext { 4 | queryDslVersion = "5.0.0" 5 | } 6 | } 7 | 8 | plugins { 9 | id 'java' 10 | id 'org.springframework.boot' version '2.7.9' 11 | id 'io.spring.dependency-management' version '1.0.15.RELEASE' 12 | id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10' 13 | } 14 | 15 | group = 'mpersand' 16 | version = '0.0.1-SNAPSHOT' 17 | sourceCompatibility = '11' 18 | 19 | configurations { 20 | compileOnly { 21 | extendsFrom annotationProcessor 22 | } 23 | } 24 | 25 | repositories { 26 | mavenCentral() 27 | //gauth 28 | maven { url 'https://jitpack.io' } 29 | } 30 | 31 | dependencies { 32 | 33 | //web 34 | implementation 'org.springframework.boot:spring-boot-starter-web' 35 | implementation 'org.springframework.boot:spring-boot-starter-validation' 36 | 37 | //redis 38 | implementation 'org.springframework.boot:spring-boot-starter-data-redis' 39 | 40 | //security 41 | implementation 'org.springframework.boot:spring-boot-starter-security' 42 | 43 | //lombok 44 | compileOnly 'org.projectlombok:lombok' 45 | annotationProcessor 'org.projectlombok:lombok' 46 | 47 | //db 48 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 49 | runtimeOnly 'org.mariadb.jdbc:mariadb-java-client:3.1.2' 50 | 51 | //test 52 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 53 | testImplementation 'org.springframework.security:spring-security-test' 54 | 55 | //jjwt 56 | implementation 'io.jsonwebtoken:jjwt-api:0.11.1' 57 | runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.1' 58 | runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.1' 59 | 60 | //mail 61 | implementation 'org.springframework.boot:spring-boot-starter-mail' 62 | 63 | //aws 64 | implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' 65 | 66 | //gauth 67 | implementation 'com.github.GSM-MSG:GAuth-SDK-Java:v3.0.0' 68 | 69 | //webhook 70 | implementation 'com.squareup.okhttp3:okhttp:4.9.1' 71 | 72 | //querydsl 73 | implementation "com.querydsl:querydsl-jpa:${queryDslVersion}" 74 | annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}" 75 | } 76 | 77 | tasks.named('test') { 78 | useJUnitPlatform() 79 | } 80 | 81 | //querydsl 82 | def querydslSrcDir = "$buildDir/generated/querydsl" 83 | querydsl { 84 | jpa = true 85 | querydslSourcesDir = querydslSrcDir 86 | } 87 | sourceSets { 88 | main.java.srcDir querydslSrcDir 89 | } 90 | configurations { 91 | querydsl.extendsFrom compileClasspath 92 | } 93 | compileQuerydsl { 94 | options.annotationProcessorPath = configurations.querydsl 95 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gmu-Wiki/Gmu-Wiki-Back/e8cd1a8247cef522c0a49e5d035a88be1e77ed8f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Stop when "xargs" is not available. 209 | if ! command -v xargs >/dev/null 2>&1 210 | then 211 | die "xargs is not available" 212 | fi 213 | 214 | # Use "xargs" to parse quoted args. 215 | # 216 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 217 | # 218 | # In Bash we could simply go: 219 | # 220 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 221 | # set -- "${ARGS[@]}" "$@" 222 | # 223 | # but POSIX shell has neither arrays nor command substitution, so instead we 224 | # post-process each arg (as a line of input to sed) to backslash-escape any 225 | # character that might be a shell metacharacter, then use eval to reverse 226 | # that process (while maintaining the separation between arguments), and wrap 227 | # the whole thing up as a single "set" statement. 228 | # 229 | # This will of course break if any of these variables contains a newline or 230 | # an unmatched quote. 231 | # 232 | 233 | eval "set -- $( 234 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 235 | xargs -n1 | 236 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 237 | tr '\n' ' ' 238 | )" '"$@"' 239 | 240 | exec "$JAVACMD" "$@" 241 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if %ERRORLEVEL% equ 0 goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'G-muwiki' 2 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/GMuwikiApplication.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 7 | import org.springframework.cache.annotation.EnableCaching; 8 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 9 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 10 | import org.springframework.scheduling.annotation.EnableScheduling; 11 | 12 | import java.util.TimeZone; 13 | 14 | @EnableCaching 15 | @EnableScheduling 16 | @EnableJpaAuditing 17 | @EnableJpaRepositories 18 | @SpringBootApplication 19 | @ConfigurationPropertiesScan 20 | @EnableConfigurationProperties 21 | public class GMuwikiApplication { 22 | 23 | public static void main(String[] args) { 24 | TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); 25 | SpringApplication.run(GMuwikiApplication.class, args); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/entity/RefreshToken.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.entity; 2 | 3 | import lombok.*; 4 | import org.springframework.data.annotation.Id; 5 | import org.springframework.data.redis.core.RedisHash; 6 | import org.springframework.data.redis.core.index.Indexed; 7 | 8 | import java.util.UUID; 9 | 10 | @Getter 11 | @Builder 12 | @RedisHash(value = "refreshToken", timeToLive = 60L * 60 * 24 * 7) 13 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 14 | @AllArgsConstructor 15 | public class RefreshToken { 16 | 17 | @Indexed 18 | @Id 19 | private String token; 20 | 21 | @Indexed 22 | private UUID userId; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/exception/RoleNotExistException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class RoleNotExistException extends GmuwikiException { 9 | 10 | public RoleNotExistException() { 11 | super(ErrorCode.ROLE_NOT_EXIST); 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/presentation/AuthController.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.presentation; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.auth.presentation.data.dto.LoginDto; 5 | import mpersand.Gmuwiki.domain.auth.presentation.data.request.LoginRequest; 6 | import mpersand.Gmuwiki.domain.auth.presentation.data.response.LoginResponse; 7 | import mpersand.Gmuwiki.domain.auth.presentation.data.response.NewTokenResponse; 8 | import mpersand.Gmuwiki.domain.auth.sevice.TokenReissueService; 9 | import mpersand.Gmuwiki.domain.auth.sevice.UserLoginService; 10 | import mpersand.Gmuwiki.domain.auth.sevice.UserLogoutService; 11 | import mpersand.Gmuwiki.domain.auth.util.AuthConverter; 12 | import mpersand.Gmuwiki.global.annotation.RestRequestService; 13 | import org.springframework.http.HttpStatus; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import javax.validation.Valid; 18 | import java.io.IOException; 19 | 20 | @RequiredArgsConstructor 21 | @RestRequestService("/auth") 22 | public class AuthController { 23 | 24 | private final UserLoginService userLoginService; 25 | 26 | private final UserLogoutService userLogoutService; 27 | 28 | private final TokenReissueService tokenReissueService; 29 | 30 | private final AuthConverter authConverter; 31 | 32 | @PostMapping 33 | public ResponseEntity login(@RequestBody @Valid LoginRequest loginRequest) throws IOException { 34 | 35 | LoginDto loginDto = authConverter.toDto(loginRequest); 36 | 37 | LoginResponse loginResponse = userLoginService.execute(loginDto); 38 | 39 | return new ResponseEntity<>(loginResponse, HttpStatus.OK); 40 | } 41 | 42 | @PatchMapping 43 | public ResponseEntity reIssueToken(@RequestHeader("Refresh-Token") String refreshToken) { 44 | 45 | NewTokenResponse newTokenResponse = tokenReissueService.execute(refreshToken); 46 | 47 | return new ResponseEntity<>(newTokenResponse, HttpStatus.OK); 48 | } 49 | 50 | @DeleteMapping 51 | public ResponseEntity logout(){ 52 | 53 | userLogoutService.execute(); 54 | 55 | return new ResponseEntity<>(HttpStatus.NO_CONTENT); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/presentation/data/dto/LoginDto.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.presentation.data.dto; 2 | 3 | import lombok.*; 4 | 5 | @Getter 6 | @Builder 7 | @AllArgsConstructor 8 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 9 | public class LoginDto { 10 | 11 | private String code; 12 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/presentation/data/request/LoginRequest.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.presentation.data.request; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.validation.constraints.NotBlank; 9 | 10 | @Getter 11 | @AllArgsConstructor 12 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 13 | public class LoginRequest { 14 | 15 | @NotBlank 16 | private String code; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/presentation/data/response/LoginResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.presentation.data.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.*; 5 | 6 | import java.time.ZonedDateTime; 7 | 8 | @Getter 9 | @Builder 10 | @AllArgsConstructor 11 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 12 | public class LoginResponse { 13 | 14 | private String accessToken; 15 | 16 | private String refreshToken; 17 | 18 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") 19 | private ZonedDateTime accessExp; 20 | 21 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") 22 | private ZonedDateTime refreshExp; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/presentation/data/response/NewTokenResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.presentation.data.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.*; 5 | 6 | import java.time.ZonedDateTime; 7 | 8 | @Getter 9 | @Builder 10 | @AllArgsConstructor 11 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 12 | public class NewTokenResponse { 13 | 14 | private String accessToken; 15 | 16 | private String refreshToken; 17 | 18 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") 19 | private ZonedDateTime accessExp; 20 | 21 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") 22 | private ZonedDateTime refreshExp; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/repository/RefreshTokenRepository.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.repository; 2 | 3 | import mpersand.Gmuwiki.domain.auth.entity.RefreshToken; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | import java.util.UUID; 7 | 8 | public interface RefreshTokenRepository extends CrudRepository { 9 | 10 | RefreshToken findByUserId(UUID userId); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/sevice/TokenReissueService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.sevice; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.auth.entity.RefreshToken; 5 | import mpersand.Gmuwiki.domain.auth.presentation.data.response.NewTokenResponse; 6 | import mpersand.Gmuwiki.domain.auth.repository.RefreshTokenRepository; 7 | import mpersand.Gmuwiki.domain.auth.util.AuthConverter; 8 | import mpersand.Gmuwiki.domain.user.entity.User; 9 | import mpersand.Gmuwiki.domain.user.enums.Role; 10 | import mpersand.Gmuwiki.domain.user.exception.UserNotFoundException; 11 | import mpersand.Gmuwiki.domain.user.repository.UserRepository; 12 | import mpersand.Gmuwiki.global.annotation.RollbackService; 13 | import mpersand.Gmuwiki.global.security.exception.RefreshTokenExpirationException; 14 | import mpersand.Gmuwiki.global.security.exception.TokenNotValidException; 15 | import mpersand.Gmuwiki.global.security.jwt.TokenProvider; 16 | 17 | import java.time.ZonedDateTime; 18 | 19 | @RollbackService 20 | @RequiredArgsConstructor 21 | public class TokenReissueService { 22 | 23 | private final TokenProvider tokenProvider; 24 | 25 | private final RefreshTokenRepository refreshTokenRepository; 26 | 27 | private final AuthConverter authConverter; 28 | 29 | private final UserRepository userRepository; 30 | 31 | public NewTokenResponse execute(String refreshToken) { 32 | 33 | String refresh = tokenProvider.parseToken(refreshToken); 34 | 35 | if(refresh == null) { 36 | throw new TokenNotValidException(); 37 | } 38 | 39 | String email = tokenProvider.exactEmailFromRefreshToken(refresh); 40 | 41 | Role role = tokenProvider.exactRoleFromRefreshToken(refresh); 42 | 43 | RefreshToken existingRefreshToken = refreshTokenRepository.findById(refresh) 44 | .orElseThrow(()->new RefreshTokenExpirationException()); 45 | 46 | String newAccessToken = tokenProvider.generateAccessToken(email, role); 47 | 48 | String newRefreshToken = tokenProvider.generateRefreshToken(email, role); 49 | 50 | ZonedDateTime accessExp = tokenProvider.accessExpiredTime(); 51 | 52 | ZonedDateTime refreshExp = tokenProvider.refreshExpiredTime(); 53 | 54 | RefreshToken newRefreshTokenEntity = authConverter.toEntity(existingRefreshToken.getUserId(), newRefreshToken); 55 | 56 | refreshTokenRepository.save(newRefreshTokenEntity); 57 | 58 | User user = userRepository.findByEmail(email) 59 | .orElseThrow(() -> new UserNotFoundException()); 60 | 61 | return NewTokenResponse.builder() 62 | .accessToken(newAccessToken) 63 | .refreshToken(newRefreshToken) 64 | .accessExp(accessExp) 65 | .refreshExp(refreshExp) 66 | .build(); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/sevice/UserLoginService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.sevice; 2 | 3 | import gauth.GAuth; 4 | import gauth.GAuthToken; 5 | import gauth.GAuthUserInfo; 6 | import lombok.RequiredArgsConstructor; 7 | import mpersand.Gmuwiki.domain.auth.exception.RoleNotExistException; 8 | import mpersand.Gmuwiki.domain.auth.presentation.data.dto.LoginDto; 9 | import mpersand.Gmuwiki.domain.auth.presentation.data.response.LoginResponse; 10 | import mpersand.Gmuwiki.domain.auth.util.AuthUtil; 11 | import mpersand.Gmuwiki.domain.user.entity.User; 12 | import mpersand.Gmuwiki.domain.user.enums.Role; 13 | import mpersand.Gmuwiki.domain.user.repository.UserRepository; 14 | import mpersand.Gmuwiki.global.annotation.RollbackService; 15 | import mpersand.Gmuwiki.global.gauth.properties.GAuthProperties; 16 | import mpersand.Gmuwiki.global.security.jwt.TokenProvider; 17 | 18 | import java.io.IOException; 19 | import java.time.ZonedDateTime; 20 | 21 | @RequiredArgsConstructor 22 | @RollbackService 23 | public class UserLoginService { 24 | 25 | private final GAuthProperties gAuthProperties; 26 | 27 | private final UserRepository userRepository; 28 | 29 | private final TokenProvider tokenProvider; 30 | 31 | private final GAuth gAuth; 32 | 33 | private final AuthUtil authUtil; 34 | 35 | public LoginResponse execute(LoginDto loginDto) throws IOException { 36 | 37 | GAuthToken gAuthToken = gAuth.generateToken(loginDto.getCode(), gAuthProperties.getClientId(), gAuthProperties.getClientSecret(), gAuthProperties.getRedirectUri()); 38 | 39 | GAuthUserInfo gAuthUserInfo = gAuth.getUserInfo(gAuthToken.getAccessToken()); 40 | 41 | Role role = getRoleByGAuthInfo(gAuthUserInfo.getRole(), gAuthUserInfo.getEmail()); 42 | 43 | String accessToken = tokenProvider.generateAccessToken(gAuthUserInfo.getEmail(), role); 44 | 45 | String refreshToken = tokenProvider.generateRefreshToken(gAuthUserInfo.getEmail(), role); 46 | 47 | ZonedDateTime accessExp = tokenProvider.accessExpiredTime(); 48 | 49 | ZonedDateTime refreshExp = tokenProvider.refreshExpiredTime(); 50 | 51 | if(role == Role.ROLE_ADMIN) { 52 | createAdminOrRefreshToken(gAuthUserInfo, refreshToken); 53 | } else if (role == Role.ROLE_GRADUATE) { 54 | createGraduateOrRefreshToken(gAuthUserInfo, refreshToken); 55 | } else { 56 | createUserOrRefreshToken(gAuthUserInfo, refreshToken); 57 | } 58 | 59 | return LoginResponse.builder() 60 | .accessToken(accessToken) 61 | .refreshToken(refreshToken) 62 | .accessExp(accessExp) 63 | .refreshExp(refreshExp) 64 | .build(); 65 | } 66 | 67 | private Role getRoleByGAuthInfo(String role, String email) { 68 | 69 | String secretEmail = System.getenv("SECRET_EMAIL"); 70 | 71 | if (email.equals(secretEmail)) { 72 | return Role.ROLE_ADMIN; 73 | } 74 | 75 | User user = userRepository.findUserByEmail(email); 76 | 77 | if(user == null) { 78 | switch (role) { 79 | case "ROLE_STUDENT": 80 | return Role.ROLE_STUDENT; 81 | case "ROLE_ADMIN": 82 | return Role.ROLE_ADMIN; 83 | case "ROLE_GRADUATE": 84 | return Role.ROLE_GRADUATE; 85 | default: 86 | throw new RoleNotExistException(); 87 | } 88 | } 89 | 90 | if(user.getRole().equals(Role.ROLE_ADMIN)) { 91 | 92 | return Role.ROLE_ADMIN; 93 | } 94 | return Role.ROLE_STUDENT; 95 | } 96 | 97 | private void createUserOrRefreshToken(GAuthUserInfo gAuthUserInfo, String refreshToken) { 98 | 99 | User userInfo = userRepository.findUserByEmail(gAuthUserInfo.getEmail()); 100 | 101 | if(userInfo == null) { 102 | 103 | authUtil.saveNewUser(gAuthUserInfo, refreshToken); 104 | } else { 105 | 106 | authUtil.saveNewRefreshToken(userInfo, refreshToken); 107 | } 108 | } 109 | 110 | private void createGraduateOrRefreshToken(GAuthUserInfo gAuthUserInfo, String refreshToken) { 111 | 112 | User userInfo = userRepository.findUserByEmail(gAuthUserInfo.getEmail()); 113 | 114 | if(userInfo == null) { 115 | 116 | authUtil.saveNewGraduate(gAuthUserInfo, refreshToken); 117 | } else { 118 | 119 | authUtil.saveNewRefreshToken(userInfo, refreshToken); 120 | } 121 | } 122 | 123 | private void createAdminOrRefreshToken(GAuthUserInfo gAuthUserInfo, String refreshToken) { 124 | 125 | User adminInfo = userRepository.findUserByEmail(gAuthUserInfo.getEmail()); 126 | 127 | if(adminInfo == null) { 128 | 129 | authUtil.saveNewAdmin(gAuthUserInfo, refreshToken); 130 | } else { 131 | 132 | authUtil.saveNewRefreshToken(adminInfo, refreshToken); 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/sevice/UserLogoutService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.sevice; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.auth.entity.RefreshToken; 5 | import mpersand.Gmuwiki.domain.auth.repository.RefreshTokenRepository; 6 | import mpersand.Gmuwiki.domain.user.entity.User; 7 | import mpersand.Gmuwiki.domain.user.exception.UserNotFoundException; 8 | import mpersand.Gmuwiki.global.annotation.RollbackService; 9 | import mpersand.Gmuwiki.global.util.UserUtil; 10 | 11 | @RollbackService 12 | @RequiredArgsConstructor 13 | public class UserLogoutService { 14 | 15 | private final UserUtil userUtil; 16 | 17 | private final RefreshTokenRepository refreshTokenRepository; 18 | 19 | public void execute() { 20 | 21 | User userInfo = userUtil.currentUser(); 22 | 23 | RefreshToken refreshToken = refreshTokenRepository.findByUserId(userInfo.getId()); 24 | 25 | if(refreshToken == null) { 26 | 27 | throw new UserNotFoundException(); 28 | } 29 | 30 | refreshTokenRepository.delete(refreshToken); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/util/AuthConverter.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.util; 2 | 3 | import gauth.GAuthUserInfo; 4 | import mpersand.Gmuwiki.domain.auth.entity.RefreshToken; 5 | import mpersand.Gmuwiki.domain.auth.presentation.data.dto.LoginDto; 6 | import mpersand.Gmuwiki.domain.auth.presentation.data.request.LoginRequest; 7 | import mpersand.Gmuwiki.domain.user.entity.User; 8 | import mpersand.Gmuwiki.domain.user.enums.Role; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.UUID; 12 | 13 | @Component 14 | public class AuthConverter { 15 | 16 | public LoginDto toDto(LoginRequest loginRequest) { 17 | 18 | LoginDto loginDto = LoginDto.builder() 19 | .code(loginRequest.getCode()) 20 | .build(); 21 | 22 | return loginDto; 23 | } 24 | 25 | 26 | public User toEntity(GAuthUserInfo gAuthUserInfo) { 27 | 28 | User user = User.builder() 29 | .id(UUID.randomUUID()) 30 | .email(gAuthUserInfo.getEmail()) 31 | .name(gAuthUserInfo.getName()) 32 | .grade(gAuthUserInfo.getGrade()) 33 | .classNum(gAuthUserInfo.getClassNum()) 34 | .stuNum(gAuthUserInfo.getNum()) 35 | .role(Role.ROLE_STUDENT) 36 | .build(); 37 | 38 | return user; 39 | } 40 | 41 | public User toGraduateEntity(GAuthUserInfo gAuthUserInfo) { 42 | 43 | User user = User.builder() 44 | .id(UUID.randomUUID()) 45 | .email(gAuthUserInfo.getEmail()) 46 | .name(gAuthUserInfo.getName()) 47 | .grade(gAuthUserInfo.getGrade()) 48 | .classNum(gAuthUserInfo.getClassNum()) 49 | .stuNum(gAuthUserInfo.getNum()) 50 | .role(Role.ROLE_GRADUATE) 51 | .build(); 52 | 53 | return user; 54 | } 55 | 56 | public User toAdminEntity(GAuthUserInfo gAuthUserInfo) { 57 | 58 | User user = User.builder() 59 | .id(UUID.randomUUID()) 60 | .email(gAuthUserInfo.getEmail()) 61 | .name(gAuthUserInfo.getName()) 62 | .grade(gAuthUserInfo.getGrade()) 63 | .classNum(gAuthUserInfo.getClassNum()) 64 | .stuNum(gAuthUserInfo.getNum()) 65 | .role(Role.ROLE_ADMIN) 66 | .build(); 67 | 68 | return user; 69 | } 70 | 71 | public RefreshToken toEntity(User userInfo, String refreshToken) { 72 | RefreshToken token = RefreshToken.builder() 73 | .userId(userInfo.getId()) 74 | .token(refreshToken) 75 | .build(); 76 | 77 | return token; 78 | } 79 | 80 | public RefreshToken toEntity(UUID userId, String refreshToken) { 81 | RefreshToken token = RefreshToken.builder() 82 | .userId(userId) 83 | .token(refreshToken) 84 | .build(); 85 | 86 | return token; 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/auth/util/AuthUtil.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.auth.util; 2 | 3 | import gauth.GAuthUserInfo; 4 | import lombok.AllArgsConstructor; 5 | import mpersand.Gmuwiki.domain.auth.entity.RefreshToken; 6 | import mpersand.Gmuwiki.domain.auth.repository.RefreshTokenRepository; 7 | import mpersand.Gmuwiki.domain.user.entity.User; 8 | import mpersand.Gmuwiki.domain.user.repository.UserRepository; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @AllArgsConstructor 13 | public class AuthUtil { 14 | 15 | private final RefreshTokenRepository refreshTokenRepository; 16 | 17 | private final AuthConverter authConverter; 18 | 19 | private final UserRepository userRepository; 20 | 21 | public void saveNewUser(GAuthUserInfo gAuthUserInfo, String refreshToken) { 22 | 23 | User signInUserInfo = authConverter.toEntity(gAuthUserInfo); 24 | 25 | userRepository.save(signInUserInfo); 26 | 27 | saveNewRefreshToken(signInUserInfo, refreshToken); 28 | } 29 | 30 | public void saveNewGraduate(GAuthUserInfo gAuthUserInfo, String refreshToken) { 31 | 32 | User signInGraduateInfo = authConverter.toGraduateEntity(gAuthUserInfo); 33 | 34 | userRepository.save(signInGraduateInfo); 35 | 36 | saveNewRefreshToken(signInGraduateInfo, refreshToken); 37 | } 38 | 39 | public void saveNewAdmin(GAuthUserInfo gAuthUserInfo, String refreshToken) { 40 | 41 | User signInAdminInfo = authConverter.toAdminEntity(gAuthUserInfo); 42 | 43 | userRepository.save(signInAdminInfo); 44 | 45 | saveNewRefreshToken(signInAdminInfo, refreshToken); 46 | } 47 | 48 | public RefreshToken saveNewRefreshToken(User userInfo, String refreshToken) { 49 | 50 | RefreshToken newRefreshToken = authConverter.toEntity(userInfo, refreshToken); 51 | 52 | return refreshTokenRepository.save(newRefreshToken); 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/entity/Board.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.entity; 2 | 3 | import lombok.*; 4 | import mpersand.Gmuwiki.domain.board.enums.BoardDetailType; 5 | import mpersand.Gmuwiki.domain.board.enums.BoardType; 6 | import mpersand.Gmuwiki.domain.board.presentation.dto.request.EditBoardRequest; 7 | import mpersand.Gmuwiki.domain.user.entity.User; 8 | import org.springframework.data.annotation.CreatedDate; 9 | import org.springframework.data.annotation.LastModifiedDate; 10 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 11 | 12 | import javax.persistence.*; 13 | import java.time.LocalDateTime; 14 | 15 | @Entity 16 | @Getter 17 | @Builder 18 | @AllArgsConstructor 19 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 20 | @EntityListeners(AuditingEntityListener.class) 21 | public class Board { 22 | 23 | @Id 24 | @GeneratedValue(strategy = GenerationType.IDENTITY) 25 | @Column(name = "board_id") 26 | private Long id; 27 | 28 | @Column(nullable = false) 29 | private String title; 30 | 31 | @Column(nullable = false, length = 10000) 32 | private String content; 33 | 34 | @Column(nullable = false) 35 | private String name; 36 | 37 | @ManyToOne 38 | @JoinColumn(name = "user_id") 39 | private User user; 40 | 41 | @Enumerated(EnumType.STRING) 42 | private BoardType boardType; 43 | 44 | @Enumerated(EnumType.STRING) 45 | private BoardDetailType boardDetailType; 46 | 47 | @CreatedDate 48 | private LocalDateTime createdDate; 49 | 50 | @LastModifiedDate 51 | private LocalDateTime editedDate; 52 | 53 | public void update(EditBoardRequest editBoardRequest) { 54 | this.title = editBoardRequest.getTitle(); 55 | this.content = editBoardRequest.getContent(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/entity/BoardRecord.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.entity; 2 | 3 | import lombok.*; 4 | import mpersand.Gmuwiki.domain.board.enums.BoardType; 5 | import org.springframework.data.annotation.CreatedDate; 6 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 7 | 8 | import javax.persistence.*; 9 | import java.time.LocalDateTime; 10 | 11 | @Entity 12 | @Getter 13 | @Builder 14 | @AllArgsConstructor 15 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 16 | @EntityListeners(AuditingEntityListener.class) 17 | public class BoardRecord { 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | @Column(name = "board_record_id") 22 | private Long id; 23 | 24 | @Column(nullable = false) 25 | private String title; 26 | 27 | @Column(nullable = false, length = 10000) 28 | private String content; 29 | 30 | @Column(nullable = false) 31 | private String name; 32 | 33 | @ManyToOne 34 | @JoinColumn(name = "board_id") 35 | private Board board; 36 | 37 | @Enumerated(EnumType.STRING) 38 | private BoardType boardType; 39 | 40 | @CreatedDate 41 | private LocalDateTime createdDate; 42 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/enums/BoardDetailType.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | 5 | public enum BoardDetailType { 6 | 7 | FIFTH, SIXTH, SEVENTH, EIGHTH, 8 | GENERAL, SPECIALITY, OTHER, 9 | MAJOR, CA, 10 | MAJORS, 11 | TWENTY_FIRST, TWENTY_SECOND, TWENTY_THIRD, TWENTY_FOURTH, 12 | JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEPT, OCT, NOV, DEC, 13 | TEAM, INDIVIDUAL; 14 | 15 | @JsonCreator 16 | public static BoardDetailType from(String s) { return BoardDetailType.valueOf(s.toUpperCase()); } 17 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/enums/BoardType.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | 5 | public enum BoardType { 6 | 7 | STUDENT, TEACHER, CLUB, MAJOR, INCIDENT, SCHEDULE, PROJECT; 8 | 9 | @JsonCreator 10 | public static BoardType from(String s) { return BoardType.valueOf(s.toUpperCase()); } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/exception/BoardAuthorMismatchException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class BoardAuthorMismatchException extends GmuwikiException { 9 | 10 | public BoardAuthorMismatchException() { 11 | super(ErrorCode.MISMATCH_BOARD_AUTHOR); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/exception/BoardNotChangeException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class BoardNotChangeException extends GmuwikiException { 9 | 10 | public BoardNotChangeException() { 11 | super(ErrorCode.BOARD_NOT_CHANGE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/exception/BoardNotFoundException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class BoardNotFoundException extends GmuwikiException { 9 | 10 | public BoardNotFoundException() { 11 | super(ErrorCode.BOARD_NOT_FOUND); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/exception/BoardRecordNotFoundException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class BoardRecordNotFoundException extends GmuwikiException { 9 | 10 | public BoardRecordNotFoundException() { 11 | super(ErrorCode.BOARD_RECORD_NOT_FOUND); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/exception/ExistTitleException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class ExistTitleException extends GmuwikiException { 9 | 10 | public ExistTitleException() { 11 | super(ErrorCode.ALREADY_EXIST_TITLE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/BoardController.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.enums.BoardType; 5 | import mpersand.Gmuwiki.domain.board.presentation.dto.request.CreateBoardRequest; 6 | import mpersand.Gmuwiki.domain.board.presentation.dto.request.EditBoardRequest; 7 | import mpersand.Gmuwiki.domain.board.presentation.dto.request.SearchBoardTitleRequest; 8 | import mpersand.Gmuwiki.domain.board.presentation.dto.response.*; 9 | import mpersand.Gmuwiki.domain.board.service.*; 10 | import mpersand.Gmuwiki.global.annotation.RestRequestService; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import javax.validation.Valid; 16 | 17 | @RequiredArgsConstructor 18 | @RestRequestService("/board") 19 | public class BoardController { 20 | 21 | private final CreateBoardService createBoardService; 22 | 23 | private final ListBoardService listBoardService; 24 | 25 | private final GetBoardDetailService getBoardDetailService; 26 | 27 | private final DeleteBoardService deleteBoardService; 28 | 29 | private final EditBoardService editBoardService; 30 | 31 | private final ListBoardRecordService listBoardRecordService; 32 | 33 | private final GetBoardRecordDetailService getBoardRecordDetailService; 34 | 35 | private final ListSearchBoardTitleService listSearchBoardTitleService; 36 | 37 | private final ListRecentEditBoardService listRecentEditBoardService; 38 | 39 | private final DeleteBoardRecordService deleteBoardRecordService; 40 | 41 | @PostMapping 42 | public ResponseEntity create(@RequestBody @Valid CreateBoardRequest createBoardRequest) { 43 | createBoardService.execute(createBoardRequest); 44 | return new ResponseEntity<>(HttpStatus.CREATED); 45 | } 46 | 47 | @GetMapping("/search") 48 | public ResponseEntity searchTitle(@RequestParam("title") SearchBoardTitleRequest searchBoardTitleRequest) { 49 | var list = listSearchBoardTitleService.execute(searchBoardTitleRequest); 50 | return new ResponseEntity<>(list, HttpStatus.OK); 51 | } 52 | 53 | @GetMapping("/recent") 54 | public ResponseEntity recentEditBoardTitle() { 55 | var list = listRecentEditBoardService.execute(); 56 | return new ResponseEntity<>(list, HttpStatus.OK); 57 | } 58 | 59 | @GetMapping 60 | public ResponseEntity findTypeAll(@RequestParam BoardType boardType) { 61 | var list = listBoardService.execute(boardType); 62 | return new ResponseEntity<>(list, HttpStatus.OK); 63 | } 64 | 65 | @GetMapping("/{id}/record") 66 | public ResponseEntity findRecordAll(@PathVariable Long id) { 67 | var list = listBoardRecordService.execute(id); 68 | return new ResponseEntity<>(list, HttpStatus.OK); 69 | } 70 | 71 | @GetMapping("/{id}/record/detail") 72 | public ResponseEntity findRecordDetailOne(@PathVariable Long id) { 73 | DetailBoardResponse oneFindById = getBoardRecordDetailService.execute(id); 74 | return new ResponseEntity<>(oneFindById, HttpStatus.OK); 75 | } 76 | 77 | @GetMapping("/{id}") 78 | public ResponseEntity findDetailOne(@PathVariable Long id) { 79 | DetailBoardResponse oneFindById = getBoardDetailService.execute(id); 80 | return new ResponseEntity<>(oneFindById, HttpStatus.OK); 81 | } 82 | 83 | @DeleteMapping("/{id}") 84 | public ResponseEntity delete(@PathVariable Long id) { 85 | deleteBoardService.execute(id); 86 | return new ResponseEntity<>(HttpStatus.NO_CONTENT); 87 | } 88 | 89 | @DeleteMapping("/{id}/record") 90 | public ResponseEntity deleteBoardRecord(@PathVariable Long id) { 91 | deleteBoardRecordService.execute(id); 92 | return new ResponseEntity<>(HttpStatus.NO_CONTENT); 93 | } 94 | 95 | @PatchMapping("/{id}") 96 | public ResponseEntity edit(@PathVariable Long id, @RequestBody @Valid EditBoardRequest editBoardRequest) { 97 | editBoardService.execute(id, editBoardRequest); 98 | return new ResponseEntity<>(HttpStatus.OK); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/request/CreateBoardRequest.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.request; 2 | 3 | import lombok.*; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | @Getter 8 | @AllArgsConstructor 9 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 10 | public class CreateBoardRequest { 11 | 12 | @NotBlank(message = "제목은 필수 입력값입니다.") 13 | private String title; 14 | 15 | private String content; 16 | 17 | @NotBlank(message = "글 카테고리는 필수 선택값입니다.") 18 | private String boardType; 19 | 20 | @NotBlank(message = "글 세부 카테고리는 필수 선택값입니다.") 21 | private String boardDetailType; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/request/EditBoardRequest.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.request; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.validation.constraints.NotBlank; 9 | 10 | @Getter 11 | @AllArgsConstructor 12 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 13 | public class EditBoardRequest { 14 | 15 | @NotBlank(message = "제목은 필수 입력값입니다") 16 | private String title; 17 | 18 | @NotBlank(message = "내용은 필수 입력값입니다") 19 | private String content; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/request/SearchBoardTitleRequest.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.request; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 11 | public class SearchBoardTitleRequest { 12 | 13 | private String title; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/response/BoardRecordResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import mpersand.Gmuwiki.domain.board.entity.BoardRecord; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Getter 11 | @Builder 12 | @AllArgsConstructor 13 | public class BoardRecordResponse { 14 | 15 | private Long id; 16 | 17 | private LocalDateTime createdDate; 18 | 19 | private String name; 20 | 21 | public static BoardRecordResponse toResponse(BoardRecord boardRecord) { 22 | 23 | return BoardRecordResponse.builder() 24 | .id(boardRecord.getId()) 25 | .createdDate(boardRecord.getCreatedDate()) 26 | .name(boardRecord.getName()) 27 | .build(); 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/response/BoardResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import mpersand.Gmuwiki.domain.board.entity.Board; 7 | import mpersand.Gmuwiki.domain.board.enums.BoardDetailType; 8 | 9 | @Getter 10 | @Builder 11 | @AllArgsConstructor 12 | public class BoardResponse { 13 | 14 | private Long id; 15 | 16 | private String title; 17 | 18 | private BoardDetailType boardDetailType; 19 | 20 | public static BoardResponse toResponse(Board board) { 21 | 22 | return BoardResponse.builder() 23 | .id(board.getId()) 24 | .title(board.getTitle()) 25 | .boardDetailType(board.getBoardDetailType()) 26 | .build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/response/DetailBoardResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | @Getter 10 | @Builder 11 | @AllArgsConstructor 12 | public class DetailBoardResponse { 13 | 14 | private Long id; 15 | 16 | private String title; 17 | 18 | private String content; 19 | 20 | private LocalDateTime createdDate; 21 | 22 | private LocalDateTime editedDate; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/response/ListBoardRecordResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | 7 | import java.util.List; 8 | 9 | @Getter 10 | @Builder 11 | @AllArgsConstructor 12 | public class ListBoardRecordResponse { 13 | 14 | private List boardRecordList; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/response/ListBoardResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | 7 | import java.util.List; 8 | 9 | @Getter 10 | @Builder 11 | @AllArgsConstructor 12 | public class ListBoardResponse { 13 | 14 | private List boardList; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/response/ListResentBoardResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | 7 | import java.util.List; 8 | 9 | @Getter 10 | @Builder 11 | @AllArgsConstructor 12 | public class ListResentBoardResponse { 13 | 14 | List boardTitleList; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/response/ListSearchBoardResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | 7 | import java.util.List; 8 | 9 | @Getter 10 | @Builder 11 | @AllArgsConstructor 12 | public class ListSearchBoardResponse { 13 | 14 | List boardTitleList; 15 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/response/ResentBoardResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import mpersand.Gmuwiki.domain.board.entity.Board; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Getter 11 | @Builder 12 | @AllArgsConstructor 13 | public class ResentBoardResponse { 14 | 15 | private Long id; 16 | 17 | private String title; 18 | 19 | private LocalDateTime editedDate; 20 | 21 | public static ResentBoardResponse toResponse(Board board) { 22 | 23 | return ResentBoardResponse.builder() 24 | .id(board.getId()) 25 | .title(board.getTitle()) 26 | .editedDate(board.getEditedDate()) 27 | .build(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/presentation/dto/response/SearchBoardResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import mpersand.Gmuwiki.domain.board.entity.Board; 7 | 8 | @Getter 9 | @Builder 10 | @AllArgsConstructor 11 | public class SearchBoardResponse { 12 | 13 | private Long id; 14 | 15 | private String title; 16 | 17 | public static SearchBoardResponse toResponse(Board board) { 18 | 19 | return SearchBoardResponse.builder() 20 | .id(board.getId()) 21 | .title(board.getTitle()) 22 | .build(); 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/repository/BoardRecordRepository.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.repository; 2 | 3 | import mpersand.Gmuwiki.domain.board.entity.Board; 4 | import mpersand.Gmuwiki.domain.board.entity.BoardRecord; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.List; 8 | 9 | public interface BoardRecordRepository extends JpaRepository { 10 | 11 | List findByBoard(Board board); 12 | 13 | void deleteAllByBoard(Board board); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/repository/BoardRepository.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.repository; 2 | 3 | import mpersand.Gmuwiki.domain.board.entity.Board; 4 | import mpersand.Gmuwiki.domain.board.enums.BoardType; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | import java.util.List; 8 | 9 | public interface BoardRepository extends JpaRepository, BoardRepositoryCustom { 10 | 11 | List findByBoardType(BoardType boardType); 12 | 13 | boolean existsByTitle(String title); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/repository/BoardRepositoryCustom.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.repository; 2 | 3 | import mpersand.Gmuwiki.domain.board.entity.Board; 4 | 5 | import java.util.List; 6 | 7 | public interface BoardRepositoryCustom { 8 | 9 | List findByTitle(String title); 10 | 11 | List findRecentlyModifiedBoards(); 12 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/repository/impl/BoardRepositoryCustomImpl.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.repository.impl; 2 | 3 | import com.querydsl.jpa.impl.JPAQueryFactory; 4 | import lombok.RequiredArgsConstructor; 5 | import mpersand.Gmuwiki.domain.board.entity.Board; 6 | import mpersand.Gmuwiki.domain.board.entity.QBoard; 7 | import mpersand.Gmuwiki.domain.board.repository.BoardRepositoryCustom; 8 | import org.springframework.stereotype.Repository; 9 | 10 | import java.util.List; 11 | 12 | @Repository 13 | @RequiredArgsConstructor 14 | public class BoardRepositoryCustomImpl implements BoardRepositoryCustom { 15 | 16 | private final JPAQueryFactory jpaQueryFactory; 17 | 18 | @Override 19 | public List findByTitle(String title) { 20 | 21 | return jpaQueryFactory 22 | .selectFrom(QBoard.board) 23 | .where(QBoard.board.title.contains(title)) 24 | .orderBy(QBoard.board.title.asc()) 25 | .limit(12) 26 | .fetch(); 27 | } 28 | 29 | @Override 30 | public List findRecentlyModifiedBoards() { 31 | 32 | return jpaQueryFactory 33 | .selectFrom(QBoard.board) 34 | .orderBy(QBoard.board.editedDate.desc()) 35 | .limit(12) 36 | .fetch(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/service/CreateBoardService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.Board; 5 | import mpersand.Gmuwiki.domain.board.entity.BoardRecord; 6 | import mpersand.Gmuwiki.domain.board.enums.BoardDetailType; 7 | import mpersand.Gmuwiki.domain.board.enums.BoardType; 8 | import mpersand.Gmuwiki.domain.board.exception.ExistTitleException; 9 | import mpersand.Gmuwiki.domain.board.presentation.dto.request.CreateBoardRequest; 10 | import mpersand.Gmuwiki.domain.board.repository.BoardRecordRepository; 11 | import mpersand.Gmuwiki.domain.board.repository.BoardRepository; 12 | import mpersand.Gmuwiki.domain.user.entity.User; 13 | import mpersand.Gmuwiki.global.annotation.RollbackService; 14 | import mpersand.Gmuwiki.global.util.UserUtil; 15 | 16 | import java.time.LocalDateTime; 17 | 18 | @RequiredArgsConstructor 19 | @RollbackService 20 | public class CreateBoardService { 21 | 22 | private final UserUtil userUtil; 23 | 24 | private final BoardRepository boardRepository; 25 | 26 | private final BoardRecordRepository boardRecordRepository; 27 | 28 | public void execute(CreateBoardRequest createBoardRequest) { 29 | 30 | User user = userUtil.currentUser(); 31 | 32 | if(boardRepository.existsByTitle(createBoardRequest.getTitle())) { 33 | throw new ExistTitleException(); 34 | } 35 | 36 | Board board = Board.builder() 37 | .title(createBoardRequest.getTitle()) 38 | .content(createBoardRequest.getContent()) 39 | .name(user.getName()) 40 | .boardType(BoardType.from(createBoardRequest.getBoardType())) 41 | .boardDetailType(BoardDetailType.from(createBoardRequest.getBoardDetailType())) 42 | .user(user) 43 | .createdDate(LocalDateTime.now()) 44 | .editedDate(LocalDateTime.now()) 45 | .build(); 46 | 47 | BoardRecord boardRecord = BoardRecord.builder() 48 | .title(board.getTitle()) 49 | .content(board.getContent()) 50 | .name(board.getName()) 51 | .boardType(board.getBoardType()) 52 | .createdDate(board.getCreatedDate()) 53 | .board(board) 54 | .build(); 55 | 56 | boardRepository.save(board); 57 | 58 | boardRecordRepository.save(boardRecord); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/service/DeleteBoardRecordService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.BoardRecord; 5 | import mpersand.Gmuwiki.global.annotation.RollbackService; 6 | import mpersand.Gmuwiki.global.util.BoardRecordUtil; 7 | 8 | @RequiredArgsConstructor 9 | @RollbackService 10 | public class DeleteBoardRecordService { 11 | 12 | public final BoardRecordUtil boardRecordUtil; 13 | 14 | public void execute(Long id) { 15 | 16 | BoardRecord boardRecord = boardRecordUtil.findBoardRecordById(id); 17 | 18 | boardRecordUtil.deleteBoardRecord(boardRecord); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/service/DeleteBoardService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.Board; 5 | import mpersand.Gmuwiki.domain.board.exception.BoardAuthorMismatchException; 6 | import mpersand.Gmuwiki.domain.board.repository.BoardRecordRepository; 7 | import mpersand.Gmuwiki.domain.board.repository.BoardRepository; 8 | import mpersand.Gmuwiki.domain.user.entity.User; 9 | import mpersand.Gmuwiki.domain.user.enums.Role; 10 | import mpersand.Gmuwiki.global.annotation.RollbackService; 11 | import mpersand.Gmuwiki.global.util.BoardUtil; 12 | import mpersand.Gmuwiki.global.util.UserUtil; 13 | 14 | @RequiredArgsConstructor 15 | @RollbackService 16 | public class DeleteBoardService { 17 | 18 | private final BoardRepository boardRepository; 19 | 20 | private final BoardRecordRepository boardRecordRepository; 21 | 22 | private final UserUtil userUtil; 23 | 24 | private final BoardUtil boardUtil; 25 | 26 | public void execute(Long id) { 27 | 28 | Board board = boardUtil.findBoardById(id); 29 | 30 | User user = userUtil.currentUser(); 31 | 32 | if(!(board.getUser() == user) && user.getRole().equals(Role.ROLE_STUDENT)) { 33 | 34 | throw new BoardAuthorMismatchException(); 35 | } 36 | 37 | boardRecordRepository.deleteAllByBoard(board); 38 | 39 | boardRepository.delete(board); 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/service/EditBoardService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.Board; 5 | import mpersand.Gmuwiki.domain.board.entity.BoardRecord; 6 | import mpersand.Gmuwiki.domain.board.exception.BoardNotChangeException; 7 | import mpersand.Gmuwiki.domain.board.exception.ExistTitleException; 8 | import mpersand.Gmuwiki.domain.board.presentation.dto.request.EditBoardRequest; 9 | import mpersand.Gmuwiki.domain.board.repository.BoardRecordRepository; 10 | import mpersand.Gmuwiki.domain.board.repository.BoardRepository; 11 | import mpersand.Gmuwiki.domain.user.entity.User; 12 | import mpersand.Gmuwiki.global.annotation.RollbackService; 13 | import mpersand.Gmuwiki.global.util.BoardUtil; 14 | import mpersand.Gmuwiki.global.util.UserUtil; 15 | 16 | @RequiredArgsConstructor 17 | @RollbackService 18 | public class EditBoardService { 19 | 20 | private final BoardRepository boardRepository; 21 | 22 | private final BoardRecordRepository boardRecordRepository; 23 | 24 | private final BoardUtil boardUtil; 25 | 26 | private final UserUtil userUtil; 27 | 28 | public void execute(Long id, EditBoardRequest editBoardRequest) { 29 | 30 | Board board = boardUtil.findBoardById(id); 31 | 32 | User user = userUtil.currentUser(); 33 | 34 | if (board.getTitle().equals(editBoardRequest.getTitle()) && board.getContent().equals(editBoardRequest.getContent())) { 35 | throw new BoardNotChangeException(); 36 | } else if (!board.getTitle().equals(editBoardRequest.getTitle()) && boardRepository.existsByTitle(editBoardRequest.getTitle())) { 37 | throw new ExistTitleException(); 38 | } 39 | 40 | BoardRecord boardRecord = BoardRecord.builder() 41 | .title(editBoardRequest.getTitle()) 42 | .content(editBoardRequest.getContent()) 43 | .name(user.getName()) 44 | .boardType(board.getBoardType()) 45 | .createdDate(board.getEditedDate()) 46 | .board(board) 47 | .build(); 48 | 49 | boardRecordRepository.save(boardRecord); 50 | 51 | board.update(editBoardRequest); 52 | } 53 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/service/GetBoardDetailService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.Board; 5 | import mpersand.Gmuwiki.domain.board.presentation.dto.response.DetailBoardResponse; 6 | import mpersand.Gmuwiki.global.annotation.ReadOnlyService; 7 | import mpersand.Gmuwiki.global.util.BoardUtil; 8 | 9 | @RequiredArgsConstructor 10 | @ReadOnlyService 11 | public class GetBoardDetailService { 12 | 13 | private final BoardUtil boardUtil; 14 | 15 | public DetailBoardResponse execute(Long id) { 16 | 17 | Board board = boardUtil.findBoardById(id); 18 | 19 | DetailBoardResponse detailBoardResponse = DetailBoardResponse.builder() 20 | .id(board.getId()) 21 | .title(board.getTitle()) 22 | .content(board.getContent()) 23 | .createdDate(board.getCreatedDate()) 24 | .editedDate(board.getEditedDate()) 25 | .build(); 26 | 27 | return detailBoardResponse; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/service/GetBoardRecordDetailService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.BoardRecord; 5 | import mpersand.Gmuwiki.domain.board.exception.BoardRecordNotFoundException; 6 | import mpersand.Gmuwiki.domain.board.presentation.dto.response.DetailBoardResponse; 7 | import mpersand.Gmuwiki.domain.board.repository.BoardRecordRepository; 8 | import mpersand.Gmuwiki.global.annotation.ReadOnlyService; 9 | 10 | @ReadOnlyService 11 | @RequiredArgsConstructor 12 | public class GetBoardRecordDetailService { 13 | 14 | private final BoardRecordRepository boardRecordRepository; 15 | 16 | public DetailBoardResponse execute(Long id) { 17 | 18 | BoardRecord boardRecord = boardRecordRepository.findById(id) 19 | .orElseThrow(() -> new BoardRecordNotFoundException()); 20 | 21 | DetailBoardResponse detailBoardResponse = DetailBoardResponse.builder() 22 | .id(boardRecord.getId()) 23 | .title(boardRecord.getTitle()) 24 | .content(boardRecord.getContent()) 25 | .createdDate(boardRecord.getCreatedDate()) 26 | .build(); 27 | 28 | return detailBoardResponse; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/service/ListBoardRecordService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.Board; 5 | import mpersand.Gmuwiki.domain.board.entity.BoardRecord; 6 | import mpersand.Gmuwiki.domain.board.presentation.dto.response.ListBoardRecordResponse; 7 | import mpersand.Gmuwiki.domain.board.repository.BoardRecordRepository; 8 | import mpersand.Gmuwiki.global.annotation.ReadOnlyService; 9 | import mpersand.Gmuwiki.global.util.BoardUtil; 10 | 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | import static mpersand.Gmuwiki.domain.board.presentation.dto.response.BoardRecordResponse.toResponse; 15 | 16 | 17 | @RequiredArgsConstructor 18 | @ReadOnlyService 19 | public class ListBoardRecordService { 20 | 21 | private final BoardRecordRepository boardRecordRepository; 22 | 23 | private final BoardUtil boardUtil; 24 | 25 | public ListBoardRecordResponse execute(Long id) { 26 | 27 | Board board = boardUtil.findBoardById(id); 28 | 29 | List boardRecords = boardRecordRepository.findByBoard(board); 30 | 31 | ListBoardRecordResponse listBoardRecordResponse = ListBoardRecordResponse.builder() 32 | .boardRecordList( 33 | boardRecords.stream() 34 | .map(boardRecord -> toResponse(boardRecord)) 35 | .collect(Collectors.toList()) 36 | ) 37 | .build(); 38 | 39 | return listBoardRecordResponse; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/service/ListBoardService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.Board; 5 | import mpersand.Gmuwiki.domain.board.enums.BoardType; 6 | import mpersand.Gmuwiki.domain.board.presentation.dto.response.ListBoardResponse; 7 | import mpersand.Gmuwiki.domain.board.repository.BoardRepository; 8 | import mpersand.Gmuwiki.global.annotation.ReadOnlyService; 9 | 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | import static mpersand.Gmuwiki.domain.board.presentation.dto.response.BoardResponse.toResponse; 14 | 15 | @RequiredArgsConstructor 16 | @ReadOnlyService 17 | public class ListBoardService { 18 | 19 | private final BoardRepository boardRepository; 20 | 21 | public ListBoardResponse execute(BoardType boardType) { 22 | 23 | List boards = boardRepository.findByBoardType(boardType); 24 | 25 | ListBoardResponse listBoardResponse = ListBoardResponse.builder() 26 | .boardList( 27 | boards.stream() 28 | .map(board -> toResponse(board)) 29 | .collect(Collectors.toList()) 30 | ) 31 | .build(); 32 | 33 | return listBoardResponse; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/service/ListRecentEditBoardService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.Board; 5 | import mpersand.Gmuwiki.domain.board.presentation.dto.response.ListResentBoardResponse; 6 | import mpersand.Gmuwiki.domain.board.repository.BoardRepository; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.transaction.annotation.Transactional; 9 | 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | import static mpersand.Gmuwiki.domain.board.presentation.dto.response.ResentBoardResponse.toResponse; 14 | 15 | @Service 16 | @Transactional(readOnly = true, rollbackFor = Exception.class) 17 | @RequiredArgsConstructor 18 | public class ListRecentEditBoardService { 19 | 20 | private final BoardRepository boardRepository; 21 | 22 | public ListResentBoardResponse execute() { 23 | 24 | List titles = boardRepository.findRecentlyModifiedBoards(); 25 | 26 | ListResentBoardResponse listResentBoardResponse = ListResentBoardResponse.builder() 27 | .boardTitleList( 28 | titles.stream() 29 | .map(board -> toResponse(board)) 30 | .collect(Collectors.toList()) 31 | ) 32 | .build(); 33 | 34 | return listResentBoardResponse; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/board/service/ListSearchBoardTitleService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.board.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.Board; 5 | import mpersand.Gmuwiki.domain.board.presentation.dto.request.SearchBoardTitleRequest; 6 | import mpersand.Gmuwiki.domain.board.presentation.dto.response.ListSearchBoardResponse; 7 | import mpersand.Gmuwiki.domain.board.repository.BoardRepository; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | import static mpersand.Gmuwiki.domain.board.presentation.dto.response.SearchBoardResponse.toResponse; 15 | 16 | @Service 17 | @Transactional(readOnly = true, rollbackFor = Exception.class) 18 | @RequiredArgsConstructor 19 | public class ListSearchBoardTitleService { 20 | 21 | private final BoardRepository boardRepository; 22 | 23 | public ListSearchBoardResponse execute(SearchBoardTitleRequest searchBoardTitleRequest) { 24 | 25 | List titles = boardRepository.findByTitle(searchBoardTitleRequest.getTitle()); 26 | 27 | ListSearchBoardResponse listSearchBoardResponse = ListSearchBoardResponse.builder() 28 | .boardTitleList( 29 | titles.stream() 30 | .map(board -> toResponse(board)) 31 | .collect(Collectors.toList()) 32 | ) 33 | .build(); 34 | 35 | return listSearchBoardResponse; 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/file/exception/FileUploadFailedException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.file.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class FileUploadFailedException extends GmuwikiException { 9 | 10 | public FileUploadFailedException() { 11 | super(ErrorCode.FILE_UPLOAD_FAIL); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/file/exception/InvalidFormatFileException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.file.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class InvalidFormatFileException extends GmuwikiException { 9 | 10 | public InvalidFormatFileException() { 11 | super(ErrorCode.INVALID_FORMAT_FILE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/file/exception/NotAllowedFileException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.file.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class NotAllowedFileException extends GmuwikiException { 9 | 10 | public NotAllowedFileException() { 11 | super(ErrorCode.NOT_ALLOWED_FILE); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/file/presentation/FileController.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.file.presentation; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.file.presentation.dto.response.FileUploadResponse; 5 | import mpersand.Gmuwiki.domain.file.service.FileUploadService; 6 | import mpersand.Gmuwiki.global.annotation.RestRequestService; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestPart; 11 | import org.springframework.web.multipart.MultipartFile; 12 | 13 | @RequiredArgsConstructor 14 | @RestRequestService("/file") 15 | public class FileController { 16 | 17 | private final FileUploadService fileUploadService; 18 | 19 | @PostMapping 20 | public ResponseEntity upload(@RequestPart(name = "file", required = false) MultipartFile files) { 21 | FileUploadResponse fileUploadResponse = fileUploadService.execute(files); 22 | return new ResponseEntity<>(fileUploadResponse, HttpStatus.CREATED); 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/file/presentation/dto/response/FileUploadResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.file.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | 7 | @Getter 8 | @Builder 9 | @AllArgsConstructor 10 | public class FileUploadResponse { 11 | 12 | private String awsUrl; 13 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/file/service/FileUploadService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.file.service; 2 | 3 | import com.amazonaws.services.s3.AmazonS3; 4 | import com.amazonaws.services.s3.model.CannedAccessControlList; 5 | import com.amazonaws.services.s3.model.DeleteObjectRequest; 6 | import com.amazonaws.services.s3.model.ObjectMetadata; 7 | import com.amazonaws.services.s3.model.PutObjectRequest; 8 | import lombok.RequiredArgsConstructor; 9 | import mpersand.Gmuwiki.domain.file.exception.FileUploadFailedException; 10 | import mpersand.Gmuwiki.domain.file.exception.InvalidFormatFileException; 11 | import mpersand.Gmuwiki.domain.file.exception.NotAllowedFileException; 12 | import mpersand.Gmuwiki.domain.file.presentation.dto.response.FileUploadResponse; 13 | import mpersand.Gmuwiki.global.annotation.RollbackService; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.web.multipart.MultipartFile; 16 | 17 | import java.io.IOException; 18 | import java.io.InputStream; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | import java.util.UUID; 22 | 23 | @RequiredArgsConstructor 24 | @RollbackService 25 | public class FileUploadService { 26 | 27 | @Value("${cloud.aws.s3.bucket}") 28 | private String bucket; 29 | 30 | private final AmazonS3 amazonS3; 31 | 32 | private static final List ALLOWED_MIME_TYPES = Arrays.asList("image/jpeg", "image/png", "image/gif", "image/svg+xml"); 33 | 34 | public FileUploadResponse execute(MultipartFile file) { 35 | 36 | String fileName = createFileName(file.getOriginalFilename()); 37 | 38 | String contentType = file.getContentType(); 39 | if (!ALLOWED_MIME_TYPES.contains(contentType)) { 40 | throw new NotAllowedFileException(); 41 | } 42 | 43 | ObjectMetadata objectMetadata = new ObjectMetadata(); 44 | 45 | objectMetadata.setContentLength(file.getSize()); 46 | 47 | objectMetadata.setContentType(file.getContentType()); 48 | 49 | try(InputStream inputStream = file.getInputStream()) { 50 | 51 | amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata) 52 | .withCannedAcl(CannedAccessControlList.PublicRead)); 53 | 54 | } catch(IOException e) { 55 | 56 | throw new FileUploadFailedException(); 57 | } 58 | 59 | String fileUrl = generateFileUrl(fileName); 60 | 61 | return FileUploadResponse.builder() 62 | .awsUrl(fileUrl) 63 | .build(); 64 | } 65 | 66 | public void deleteFile(String fileName) { 67 | 68 | amazonS3.deleteObject(new DeleteObjectRequest(bucket, fileName)); 69 | } 70 | 71 | private String createFileName(String fileName) { 72 | 73 | return UUID.randomUUID().toString().concat(getFileExtension(fileName)); 74 | } 75 | 76 | private String getFileExtension(String fileName) { 77 | 78 | try { 79 | 80 | return fileName.substring(fileName.lastIndexOf(".")); 81 | } catch (StringIndexOutOfBoundsException e) { 82 | 83 | throw new InvalidFormatFileException(); 84 | } 85 | } 86 | 87 | private String generateFileUrl(String fileName) { 88 | return amazonS3.getUrl(bucket, fileName).toString(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/entity/Inquiry.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.entity; 2 | 3 | import lombok.*; 4 | import mpersand.Gmuwiki.domain.inquiry.enums.InquiryType; 5 | import mpersand.Gmuwiki.domain.user.entity.User; 6 | import org.springframework.data.annotation.CreatedDate; 7 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 8 | 9 | import javax.persistence.*; 10 | import java.time.LocalDateTime; 11 | 12 | @Entity 13 | @Getter 14 | @Builder 15 | @AllArgsConstructor 16 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 17 | @EntityListeners(AuditingEntityListener.class) 18 | public class Inquiry { 19 | 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.IDENTITY) 22 | @Column(name = "inquiry_id") 23 | private Long id; 24 | 25 | @Column(nullable = false) 26 | private String title; 27 | 28 | @Column(nullable = false, length = 10000) 29 | private String content; 30 | 31 | @Column(nullable = false) 32 | private String name; 33 | 34 | @ManyToOne 35 | private User user; 36 | 37 | @Enumerated(EnumType.STRING) 38 | private InquiryType inquiryType; 39 | 40 | @CreatedDate 41 | private LocalDateTime createdDate; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/enums/InquiryType.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | 5 | public enum InquiryType { 6 | DELETE, QUESTION, REPORT; 7 | 8 | @JsonCreator 9 | public static InquiryType from(String s) { return InquiryType.valueOf(s.toUpperCase()); } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/exception/EmailSendFailedException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class EmailSendFailedException extends GmuwikiException { 9 | 10 | public EmailSendFailedException() { 11 | super(ErrorCode.EMAIL_SEND_FAIL); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/exception/InquiryNotFoundException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class InquiryNotFoundException extends GmuwikiException { 9 | 10 | public InquiryNotFoundException() { 11 | super(ErrorCode.INQUIRY_NOT_FOUND); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/presentation/InquiryController.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.presentation; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.inquiry.presentation.dto.request.InquirySendRequest; 5 | import mpersand.Gmuwiki.domain.inquiry.presentation.dto.request.InquiryWriteRequest; 6 | import mpersand.Gmuwiki.domain.inquiry.presentation.dto.response.DetailInquiryResponse; 7 | import mpersand.Gmuwiki.domain.inquiry.presentation.dto.response.ListInquiryResponse; 8 | import mpersand.Gmuwiki.domain.inquiry.service.*; 9 | import mpersand.Gmuwiki.global.annotation.RestRequestService; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import javax.validation.Valid; 15 | 16 | @RequiredArgsConstructor 17 | @RestRequestService("/inquiry") 18 | public class InquiryController { 19 | 20 | private final CreateInquiryService createInquiryService; 21 | 22 | private final ListInquiryService listInquiryService; 23 | 24 | private final GetInquiryDetailService getInquiryDetailService; 25 | 26 | private final InquiryApproveService inquiryApproveService; 27 | 28 | private final InquiryRefusalService inquiryRefusalService; 29 | 30 | @PostMapping 31 | public ResponseEntity create(@RequestBody @Valid InquiryWriteRequest inquiryWriteRequest) { 32 | createInquiryService.execute(inquiryWriteRequest); 33 | return new ResponseEntity<>(HttpStatus.CREATED); 34 | } 35 | 36 | @GetMapping 37 | public ResponseEntity findAll() { 38 | var list = listInquiryService.execute(); 39 | return new ResponseEntity<>(list, HttpStatus.OK); 40 | } 41 | 42 | @GetMapping("/{id}") 43 | public ResponseEntity findDetailOne(@PathVariable Long id) { 44 | DetailInquiryResponse oneFindById = getInquiryDetailService.execute(id); 45 | return new ResponseEntity<>(oneFindById, HttpStatus.OK); 46 | } 47 | 48 | @PostMapping("/approve/{id}") 49 | public ResponseEntity approveSend(@PathVariable Long id) { 50 | inquiryApproveService.execute(id); 51 | return new ResponseEntity<>(HttpStatus.CREATED); 52 | } 53 | 54 | @PostMapping("/refusal/{id}") 55 | public ResponseEntity refusalSend(@PathVariable Long id, @RequestBody @Valid InquirySendRequest inquirySendRequest) { 56 | inquiryRefusalService.execute(id, inquirySendRequest); 57 | return new ResponseEntity<>(HttpStatus.CREATED); 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/presentation/dto/request/InquirySendRequest.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.presentation.dto.request; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 11 | public class InquirySendRequest { 12 | 13 | private String comment; 14 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/presentation/dto/request/InquiryWriteRequest.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.presentation.dto.request; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.validation.constraints.NotBlank; 9 | 10 | @Getter 11 | @AllArgsConstructor 12 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 13 | public class InquiryWriteRequest { 14 | 15 | @NotBlank(message = "제목은 필수 입력값입니다.") 16 | private String title; 17 | 18 | private String content; 19 | 20 | @NotBlank(message = "글 종류는 필수 선택값입니다.") 21 | private String inquiryType; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/presentation/dto/response/DetailInquiryResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import mpersand.Gmuwiki.domain.inquiry.enums.InquiryType; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | 11 | @Getter 12 | @Builder 13 | @AllArgsConstructor 14 | public class DetailInquiryResponse { 15 | 16 | private Long id; 17 | 18 | private String title; 19 | 20 | private String content; 21 | 22 | private LocalDateTime createdDate; 23 | 24 | private InquiryType inquiryType; 25 | 26 | private String name; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/presentation/dto/response/InquiryResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import mpersand.Gmuwiki.domain.inquiry.entity.Inquiry; 7 | import mpersand.Gmuwiki.domain.inquiry.enums.InquiryType; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | @Getter 12 | @Builder 13 | @AllArgsConstructor 14 | public class InquiryResponse { 15 | 16 | private Long id; 17 | 18 | private String title; 19 | 20 | private String content; 21 | 22 | private LocalDateTime createdDate; 23 | 24 | private InquiryType inquiryType; 25 | 26 | public static InquiryResponse toResponse(Inquiry inquiry) { 27 | 28 | return InquiryResponse.builder() 29 | .id(inquiry.getId()) 30 | .title(inquiry.getTitle()) 31 | .content(inquiry.getContent()) 32 | .createdDate(inquiry.getCreatedDate()) 33 | .inquiryType(inquiry.getInquiryType()) 34 | .build(); 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/presentation/dto/response/ListInquiryResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.presentation.dto.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | 7 | import java.util.List; 8 | 9 | @Getter 10 | @Builder 11 | @AllArgsConstructor 12 | public class ListInquiryResponse { 13 | 14 | private List inquiryList; 15 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/repository/InquiryRepository.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.repository; 2 | 3 | import mpersand.Gmuwiki.domain.inquiry.entity.Inquiry; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface InquiryRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/service/CreateInquiryService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.inquiry.entity.Inquiry; 5 | import mpersand.Gmuwiki.domain.inquiry.enums.InquiryType; 6 | import mpersand.Gmuwiki.domain.inquiry.presentation.dto.request.InquiryWriteRequest; 7 | import mpersand.Gmuwiki.domain.inquiry.repository.InquiryRepository; 8 | import mpersand.Gmuwiki.domain.user.entity.User; 9 | import mpersand.Gmuwiki.global.annotation.RollbackService; 10 | import mpersand.Gmuwiki.global.util.UserUtil; 11 | import mpersand.Gmuwiki.global.webhook.util.DiscordUtil; 12 | 13 | import java.time.LocalDateTime; 14 | 15 | @RollbackService 16 | @RequiredArgsConstructor 17 | public class CreateInquiryService { 18 | 19 | private final UserUtil userUtil; 20 | 21 | private final InquiryRepository inquiryRepository; 22 | 23 | private final DiscordUtil discordUtil; 24 | 25 | public void execute(InquiryWriteRequest inquiryWriteRequest) { 26 | 27 | User user = userUtil.currentUser(); 28 | 29 | Inquiry inquiry = Inquiry.builder() 30 | .title(inquiryWriteRequest.getTitle()) 31 | .content(inquiryWriteRequest.getContent()) 32 | .name(user.getName()) 33 | .user(user) 34 | .inquiryType(InquiryType.from(inquiryWriteRequest.getInquiryType())) 35 | .createdDate(LocalDateTime.now()) 36 | .build(); 37 | 38 | inquiryRepository.save(inquiry); 39 | 40 | sendDiscordMessage(inquiryWriteRequest); 41 | } 42 | 43 | private void sendDiscordMessage(InquiryWriteRequest inquiryWriteRequest) { 44 | 45 | String inquiryMessage = discordUtil.createInquiryMessage(inquiryWriteRequest.getTitle(), inquiryWriteRequest.getInquiryType()); 46 | 47 | discordUtil.sendDiscordMessage( 48 | inquiryMessage 49 | ); 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/service/GetInquiryDetailService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.inquiry.entity.Inquiry; 5 | import mpersand.Gmuwiki.domain.inquiry.exception.InquiryNotFoundException; 6 | import mpersand.Gmuwiki.domain.inquiry.presentation.dto.response.DetailInquiryResponse; 7 | import mpersand.Gmuwiki.domain.inquiry.repository.InquiryRepository; 8 | import mpersand.Gmuwiki.global.annotation.ReadOnlyService; 9 | 10 | @ReadOnlyService 11 | @RequiredArgsConstructor 12 | public class GetInquiryDetailService { 13 | 14 | private final InquiryRepository inquiryRepository; 15 | 16 | public DetailInquiryResponse execute(Long id) { 17 | 18 | Inquiry inquiry = inquiryRepository.findById(id) 19 | .orElseThrow(() -> new InquiryNotFoundException()); 20 | 21 | DetailInquiryResponse detailInquiryResponse = DetailInquiryResponse.builder() 22 | .id(inquiry.getId()) 23 | .title(inquiry.getTitle()) 24 | .content(inquiry.getContent()) 25 | .createdDate(inquiry.getCreatedDate()) 26 | .inquiryType(inquiry.getInquiryType()) 27 | .name(inquiry.getName()) 28 | .build(); 29 | 30 | return detailInquiryResponse; 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/service/InquiryApproveService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.inquiry.exception.EmailSendFailedException; 5 | import mpersand.Gmuwiki.domain.inquiry.entity.Inquiry; 6 | import mpersand.Gmuwiki.domain.inquiry.exception.InquiryNotFoundException; 7 | import mpersand.Gmuwiki.domain.inquiry.repository.InquiryRepository; 8 | import mpersand.Gmuwiki.global.annotation.RollbackService; 9 | import org.springframework.mail.javamail.JavaMailSender; 10 | import org.springframework.mail.javamail.MimeMessageHelper; 11 | 12 | import javax.mail.MessagingException; 13 | import javax.mail.internet.MimeMessage; 14 | 15 | @RollbackService 16 | @RequiredArgsConstructor 17 | public class InquiryApproveService { 18 | 19 | private final InquiryRepository inquiryRepository; 20 | 21 | private final JavaMailSender javaMailSender; 22 | 23 | public void execute(Long id) { 24 | 25 | Inquiry inquiry = inquiryRepository.findById(id) 26 | .orElseThrow(() -> new InquiryNotFoundException()); 27 | 28 | String email = inquiry.getUser().getEmail(); 29 | 30 | String inquiryName = inquiry.getTitle(); 31 | 32 | sendInquiryEmail(email, inquiryName); 33 | 34 | inquiryRepository.delete(inquiry); 35 | } 36 | 37 | private MimeMessage sendInquiryEmail(String email, String inquiryName) { 38 | MimeMessage message = javaMailSender.createMimeMessage(); 39 | 40 | String subject = "G무위키 문의 답변이 도착했습니다!"; 41 | 42 | String content="\n" + 43 | "\n" + 44 | " \n" + 45 | " \n" + 46 | " Document\n" + 47 | " \n" + 48 | " \n" + 49 | "
\n" + 50 | "

\n" + 51 | " 안녕하세요\n" + 52 | "

\n" + 53 | " G무위키\n" + 54 | "

\n" + 55 | " 입니다!\n" + 56 | "

\n" + 57 | "
\n" + 58 | "

G무위키를 이용해주셔서 감사합니다. 귀하가 신청하신

\n" + inquiryName + "\n" + 59 | "

\n" + 60 | "

\n" + 61 | " 에 대한 문의가 승인되었습니다. 더욱 더 발전하는 G무위키가 되겠습니다.\n" + 62 | "

\n" + 63 | "

\n" + 64 | "
\n" + 65 | "

\n" + 66 | " 추가로 궁금한 사항이 있다면 디스코드 최민욱#9522으로 보내주시면\n" + 67 | " 최대한빠르게 확인하겠습니다. 감사합니다.\n" + 68 | "

\n" + 69 | "
\n" + 70 | " \n" + 71 | ""; 72 | 73 | try { 74 | MimeMessage mimeMessage = javaMailSender.createMimeMessage(); 75 | MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "utf-8"); 76 | helper.setTo(email); 77 | helper.setSubject(subject); 78 | helper.setText(content,true); 79 | javaMailSender.send(mimeMessage); 80 | } catch (MessagingException e) { 81 | throw new EmailSendFailedException(); 82 | } 83 | 84 | return message; 85 | } 86 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/service/InquiryRefusalService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.inquiry.exception.EmailSendFailedException; 5 | import mpersand.Gmuwiki.domain.inquiry.entity.Inquiry; 6 | import mpersand.Gmuwiki.domain.inquiry.exception.InquiryNotFoundException; 7 | import mpersand.Gmuwiki.domain.inquiry.presentation.dto.request.InquirySendRequest; 8 | import mpersand.Gmuwiki.domain.inquiry.repository.InquiryRepository; 9 | import mpersand.Gmuwiki.global.annotation.RollbackService; 10 | import org.springframework.mail.javamail.JavaMailSender; 11 | import org.springframework.mail.javamail.MimeMessageHelper; 12 | 13 | import javax.mail.MessagingException; 14 | import javax.mail.internet.MimeMessage; 15 | 16 | @RollbackService 17 | @RequiredArgsConstructor 18 | public class InquiryRefusalService { 19 | 20 | private final InquiryRepository inquiryRepository; 21 | 22 | private final JavaMailSender javaMailSender; 23 | 24 | public void execute(Long id, InquirySendRequest inquirySendRequest) { 25 | 26 | Inquiry inquiry = inquiryRepository.findById(id) 27 | .orElseThrow(() -> new InquiryNotFoundException()); 28 | 29 | String email = inquiry.getUser().getEmail(); 30 | 31 | String inquiryName = inquiry.getTitle(); 32 | 33 | sendInquiryEmail(email, inquiryName, inquirySendRequest.getComment()); 34 | 35 | inquiryRepository.delete(inquiry); 36 | } 37 | 38 | private MimeMessage sendInquiryEmail(String email, String inquiryName, String comment) { 39 | MimeMessage message = javaMailSender.createMimeMessage(); 40 | 41 | String subject = "G무위키 문의 답변이 도착했습니다!"; 42 | 43 | String content="\n" + 44 | " \n" + 45 | " \n" + 46 | " \n" + 47 | " Document\n" + 48 | " \n" + 49 | " \n" + 50 | "
\n" + 51 | "

\n" + 52 | " 안녕하세요!\n" + 53 | "

\n" + 54 | " G무위키\n" + 55 | "

\n" + 56 | " 입니다!\n" + 57 | "

\n" + 58 | "
\n" + 59 | "

G무위키를 이용해주셔서 감사합니다. 귀하가 신청하신

" + inquiryName + "

에 대한 문의가 거부되었습니다.

\n" + 60 | "
\n" + 61 | " \n" + 71 | "

거부된 사유는 다음과 같습니다.

\n" + 72 | "
\n" + 73 | " 거부 사유 : " + comment + " \n" + 74 | "

\n" + 75 | "
\n" + 76 | "
\n" + 77 | "
\n" + 78 | " 추가로 궁금한 사항이 있다면 디스코드 최민욱#9522으로 보내주시면 최대한\n" + 79 | " 빠르게 확인하겠습니다. 감사합니다.\n" + 80 | " \n" + 81 | " \n" + 82 | ""; 83 | 84 | 85 | try { 86 | MimeMessage mimeMessage = javaMailSender.createMimeMessage(); 87 | MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "utf-8"); 88 | helper.setTo(email); 89 | helper.setSubject(subject); 90 | helper.setText(content,true); 91 | javaMailSender.send(mimeMessage); 92 | } catch (MessagingException e) { 93 | throw new EmailSendFailedException(); 94 | } 95 | 96 | return message; 97 | } 98 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/inquiry/service/ListInquiryService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.inquiry.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.inquiry.entity.Inquiry; 5 | import mpersand.Gmuwiki.domain.inquiry.presentation.dto.response.ListInquiryResponse; 6 | import mpersand.Gmuwiki.domain.inquiry.repository.InquiryRepository; 7 | import mpersand.Gmuwiki.global.annotation.ReadOnlyService; 8 | 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | import static mpersand.Gmuwiki.domain.inquiry.presentation.dto.response.InquiryResponse.toResponse; 13 | 14 | @ReadOnlyService 15 | @RequiredArgsConstructor 16 | public class ListInquiryService { 17 | 18 | private final InquiryRepository inquiryRepository; 19 | 20 | public ListInquiryResponse execute() { 21 | 22 | List inquiries = inquiryRepository.findAll(); 23 | 24 | ListInquiryResponse listInquiryResponse = ListInquiryResponse.builder() 25 | .inquiryList( 26 | inquiries.stream() 27 | .map(inquiry -> toResponse(inquiry)) 28 | .collect(Collectors.toList()) 29 | ) 30 | .build(); 31 | 32 | return listInquiryResponse; 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/entity/Notice.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.entity; 2 | 3 | import lombok.*; 4 | import mpersand.Gmuwiki.domain.notice.presentation.dto.request.EditNoticeRequest; 5 | import mpersand.Gmuwiki.domain.user.entity.User; 6 | import org.springframework.data.annotation.CreatedDate; 7 | import org.springframework.data.annotation.LastModifiedDate; 8 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 9 | 10 | import javax.persistence.*; 11 | import java.time.LocalDateTime; 12 | 13 | @Entity 14 | @Getter 15 | @Builder 16 | @AllArgsConstructor 17 | @EntityListeners(AuditingEntityListener.class) 18 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 19 | public class Notice { 20 | 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.IDENTITY) 23 | @Column(name = "notice_id") 24 | private Long id; 25 | 26 | @Column(nullable = false) 27 | private String title; 28 | 29 | @Column(nullable = false, length = 10000) 30 | private String content; 31 | 32 | @Column(nullable = false) 33 | private String name; 34 | 35 | @ManyToOne 36 | private User user; 37 | 38 | @CreatedDate 39 | private LocalDateTime createdDate; 40 | 41 | @LastModifiedDate 42 | private LocalDateTime editedDate; 43 | 44 | public void update(EditNoticeRequest editNoticeRequest) { 45 | this.title = editNoticeRequest.getTitle(); 46 | this.content = editNoticeRequest.getContent(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/exception/NoticeNotFoundException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class NoticeNotFoundException extends GmuwikiException { 9 | public NoticeNotFoundException() { 10 | super(ErrorCode.NOTICE_NOT_FOUND); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/presentation/NoticeController.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.presentation; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.notice.presentation.dto.request.CreateNoticeRequest; 5 | import mpersand.Gmuwiki.domain.notice.presentation.dto.request.EditNoticeRequest; 6 | import mpersand.Gmuwiki.domain.notice.presentation.dto.response.NoticeDetailResponse; 7 | import mpersand.Gmuwiki.domain.notice.presentation.dto.response.NoticeListResponse; 8 | import mpersand.Gmuwiki.domain.notice.service.*; 9 | import mpersand.Gmuwiki.global.annotation.RestRequestService; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import javax.validation.Valid; 15 | 16 | @RequiredArgsConstructor 17 | @RestRequestService("/notice") 18 | public class NoticeController { 19 | 20 | private final ListNoticeService listNoticeService; 21 | private final GetNoticeDetailService getNoticeDetailService; 22 | private final CreateNoticeService createNoticeService; 23 | private final EditNoticeService editNoticeService; 24 | private final DeleteNoticeService deleteNoticeService; 25 | 26 | @GetMapping 27 | public ResponseEntity findAll(){ 28 | var list = listNoticeService.execute(); 29 | return new ResponseEntity<>(list, HttpStatus.OK); 30 | } 31 | 32 | @GetMapping("/{id}") 33 | public ResponseEntity findOne(@PathVariable("id") Long id){ 34 | NoticeDetailResponse oneFindById = getNoticeDetailService.execute(id); 35 | return new ResponseEntity<>(oneFindById,HttpStatus.OK); 36 | } 37 | 38 | @PostMapping 39 | public ResponseEntity create(@RequestBody @Valid CreateNoticeRequest createNoticeRequest){ 40 | createNoticeService.execute(createNoticeRequest); 41 | return new ResponseEntity<>(HttpStatus.CREATED); 42 | } 43 | 44 | @PatchMapping("/{id}") 45 | public ResponseEntity edit(@PathVariable Long id, @RequestBody @Valid EditNoticeRequest editNoticeRequest){ 46 | editNoticeService.execute(id, editNoticeRequest); 47 | return new ResponseEntity<>(HttpStatus.OK); 48 | } 49 | 50 | @DeleteMapping("/{id}") 51 | public ResponseEntity delete(@PathVariable Long id){ 52 | deleteNoticeService.execute(id); 53 | return new ResponseEntity<>(HttpStatus.NO_CONTENT); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/presentation/dto/request/CreateNoticeRequest.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.presentation.dto.request; 2 | 3 | import lombok.*; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | @Getter 8 | @Builder 9 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 10 | @AllArgsConstructor 11 | public class CreateNoticeRequest { 12 | 13 | @NotBlank(message = "제목은 필수 입력값입니다") 14 | private String title; 15 | 16 | private String content; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/presentation/dto/request/EditNoticeRequest.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.presentation.dto.request; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.validation.constraints.NotBlank; 9 | 10 | @Getter 11 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 12 | @AllArgsConstructor 13 | public class EditNoticeRequest { 14 | 15 | @NotBlank(message = "제목은 필수 입력값입니다") 16 | private String title; 17 | 18 | @NotBlank(message = "내용은 필수 입력값입니다") 19 | private String content; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/presentation/dto/response/NoticeDetailResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.presentation.dto.response; 2 | 3 | import lombok.*; 4 | 5 | import java.time.LocalDateTime; 6 | 7 | @Getter 8 | @Builder 9 | @AllArgsConstructor 10 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 11 | public class NoticeDetailResponse { 12 | 13 | private Long id; 14 | 15 | private String title; 16 | 17 | private String content; 18 | 19 | private LocalDateTime createdDate; 20 | 21 | private LocalDateTime editedDate; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/presentation/dto/response/NoticeListResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.presentation.dto.response; 2 | 3 | import lombok.*; 4 | 5 | import java.util.List; 6 | @Getter 7 | @Builder 8 | @AllArgsConstructor 9 | public class NoticeListResponse { 10 | 11 | private List noticeList; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/presentation/dto/response/NoticeResponse.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.presentation.dto.response; 2 | 3 | import lombok.*; 4 | import mpersand.Gmuwiki.domain.notice.entity.Notice; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | @Builder 11 | public class NoticeResponse { 12 | 13 | private Long id; 14 | 15 | private String title; 16 | 17 | private LocalDateTime createdDate; 18 | 19 | public static NoticeResponse toResponse(Notice notice) { 20 | 21 | return NoticeResponse.builder() 22 | .id(notice.getId()) 23 | .title(notice.getTitle()) 24 | .createdDate(notice.getCreatedDate()) 25 | .build(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/repository/NoticeRepository.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.repository; 2 | 3 | import mpersand.Gmuwiki.domain.notice.entity.Notice; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface NoticeRepository extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/service/CreateNoticeService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.notice.entity.Notice; 5 | import mpersand.Gmuwiki.domain.notice.presentation.dto.request.CreateNoticeRequest; 6 | import mpersand.Gmuwiki.domain.notice.repository.NoticeRepository; 7 | import mpersand.Gmuwiki.domain.user.entity.User; 8 | import mpersand.Gmuwiki.global.annotation.RollbackService; 9 | import mpersand.Gmuwiki.global.util.UserUtil; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | @RequiredArgsConstructor 14 | @RollbackService 15 | public class CreateNoticeService { 16 | 17 | private final UserUtil userUtil; 18 | 19 | private final NoticeRepository noticeRepository; 20 | 21 | public void execute(CreateNoticeRequest createNoticeRequest){ 22 | 23 | User user = userUtil.currentUser(); 24 | 25 | Notice notice = Notice.builder() 26 | .title(createNoticeRequest.getTitle()) 27 | .content(createNoticeRequest.getContent()) 28 | .name(user.getName()) 29 | .user(user) 30 | .createdDate(LocalDateTime.now()) 31 | .editedDate(LocalDateTime.now()) 32 | .build(); 33 | 34 | noticeRepository.save(notice); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/service/DeleteNoticeService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.notice.entity.Notice; 5 | import mpersand.Gmuwiki.domain.notice.repository.NoticeRepository; 6 | import mpersand.Gmuwiki.global.annotation.RollbackService; 7 | import mpersand.Gmuwiki.global.util.NoticeUtil; 8 | 9 | @RequiredArgsConstructor 10 | @RollbackService 11 | public class DeleteNoticeService { 12 | 13 | private final NoticeRepository noticeRepository; 14 | 15 | private final NoticeUtil noticeUtil; 16 | 17 | public void execute(Long id){ 18 | 19 | Notice notice = noticeUtil.findNoticeById(id); 20 | 21 | noticeRepository.delete(notice); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/service/EditNoticeService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.notice.entity.Notice; 5 | import mpersand.Gmuwiki.domain.notice.presentation.dto.request.EditNoticeRequest; 6 | import mpersand.Gmuwiki.global.annotation.RollbackService; 7 | import mpersand.Gmuwiki.global.util.NoticeUtil; 8 | 9 | @RequiredArgsConstructor 10 | @RollbackService 11 | public class EditNoticeService { 12 | 13 | private final NoticeUtil noticeUtil; 14 | 15 | public void execute(Long id, EditNoticeRequest editNoticeRequest){ 16 | 17 | Notice notice = noticeUtil.findNoticeById(id); 18 | 19 | notice.update(editNoticeRequest); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/service/GetNoticeDetailService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.notice.entity.Notice; 5 | import mpersand.Gmuwiki.domain.notice.presentation.dto.response.NoticeDetailResponse; 6 | import mpersand.Gmuwiki.global.annotation.ReadOnlyService; 7 | import mpersand.Gmuwiki.global.util.NoticeUtil; 8 | 9 | @RequiredArgsConstructor 10 | @ReadOnlyService 11 | public class GetNoticeDetailService { 12 | 13 | private final NoticeUtil noticeUtil; 14 | 15 | public NoticeDetailResponse execute(Long id) { 16 | 17 | Notice notice = noticeUtil.findNoticeById(id); 18 | 19 | NoticeDetailResponse noticeDetailResponse = NoticeDetailResponse.builder() 20 | .id(notice.getId()) 21 | .title(notice.getTitle()) 22 | .content(notice.getContent()) 23 | .createdDate(notice.getCreatedDate()) 24 | .editedDate(notice.getEditedDate()) 25 | .build(); 26 | 27 | return noticeDetailResponse; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/notice/service/ListNoticeService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.notice.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.notice.entity.Notice; 5 | import mpersand.Gmuwiki.domain.notice.presentation.dto.response.NoticeListResponse; 6 | import mpersand.Gmuwiki.domain.notice.repository.NoticeRepository; 7 | import mpersand.Gmuwiki.global.annotation.ReadOnlyService; 8 | 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | import static mpersand.Gmuwiki.domain.notice.presentation.dto.response.NoticeResponse.toResponse; 13 | 14 | @RequiredArgsConstructor 15 | @ReadOnlyService 16 | public class ListNoticeService { 17 | 18 | private final NoticeRepository noticeRepository; 19 | 20 | public NoticeListResponse execute() { 21 | 22 | List notices = noticeRepository.findAll(); 23 | 24 | NoticeListResponse noticeListResponse = NoticeListResponse.builder() 25 | .noticeList( 26 | notices.stream() 27 | .map(notice -> toResponse(notice)) 28 | .collect(Collectors.toList()) 29 | ) 30 | .build(); 31 | 32 | return noticeListResponse; 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/user/entity/User.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.user.entity; 2 | 3 | import lombok.*; 4 | import mpersand.Gmuwiki.domain.user.enums.Role; 5 | import org.hibernate.annotations.GenericGenerator; 6 | 7 | import javax.persistence.*; 8 | import java.util.UUID; 9 | 10 | @Entity 11 | @Getter 12 | @Builder 13 | @AllArgsConstructor 14 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 15 | public class User { 16 | 17 | @Id 18 | @GeneratedValue(generator = "uuid2") 19 | @GenericGenerator(name = "uuid2", strategy = "uuid2") 20 | @Column(columnDefinition = "BINARY(16)") 21 | private UUID id; 22 | 23 | @Column(name = "email" , nullable = false) 24 | private String email; 25 | 26 | @Column(name = "student_name" , nullable = false) 27 | private String name; 28 | 29 | @Column(name = "grade") 30 | private int grade; 31 | 32 | @Column(name = "class_num") 33 | private int classNum; 34 | 35 | @Column(name = "student_num") 36 | private int stuNum; 37 | 38 | @Column(name = "role") 39 | @Enumerated(EnumType.STRING) 40 | private Role role; 41 | 42 | public void updateRole(Role role) { 43 | this.role = role; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/user/enums/Role.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.user.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import org.springframework.security.core.GrantedAuthority; 5 | 6 | public enum Role implements GrantedAuthority { 7 | 8 | ROLE_STUDENT, ROLE_ADMIN, ROLE_GRADUATE; 9 | 10 | @JsonCreator 11 | public static Role from(String s) { 12 | return Role.valueOf(s.toUpperCase()); 13 | } 14 | 15 | @Override 16 | public String getAuthority() { 17 | return name(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/user/exception/UserNotFoundException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.user.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class UserNotFoundException extends GmuwikiException { 9 | 10 | public UserNotFoundException() { 11 | super(ErrorCode.USER_NOT_FOUND); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/user/presentation/RoleController.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.user.presentation; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.user.presentation.dto.request.RoleChangeRequest; 5 | import mpersand.Gmuwiki.domain.user.service.AdminRoleChangeService; 6 | import mpersand.Gmuwiki.domain.user.service.UserRoleChangeService; 7 | import mpersand.Gmuwiki.global.annotation.RestRequestService; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | @RequiredArgsConstructor 13 | @RestRequestService("/role") 14 | public class RoleController { 15 | 16 | private final AdminRoleChangeService adminRoleChangeService; 17 | 18 | private final UserRoleChangeService userRoleChangeService; 19 | 20 | @PatchMapping("/grant") 21 | public ResponseEntity grant(@RequestBody RoleChangeRequest roleChangeRequest) { 22 | 23 | adminRoleChangeService.execute(roleChangeRequest); 24 | 25 | return new ResponseEntity<>(HttpStatus.OK); 26 | } 27 | 28 | @PatchMapping("/revoke") 29 | public ResponseEntity revoke(@RequestBody RoleChangeRequest roleChangeRequest) { 30 | 31 | userRoleChangeService.execute(roleChangeRequest); 32 | 33 | return new ResponseEntity<>(HttpStatus.OK); 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/user/presentation/dto/request/RoleChangeRequest.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.user.presentation.dto.request; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | @NoArgsConstructor(access = AccessLevel.PROTECTED) 11 | public class RoleChangeRequest { 12 | 13 | private String email; 14 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/user/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.user.repository; 2 | 3 | import mpersand.Gmuwiki.domain.user.entity.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | import java.util.UUID; 8 | 9 | public interface UserRepository extends JpaRepository { 10 | 11 | Optional findByEmail(String email); 12 | 13 | User findUserByEmail(String email); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/user/service/AdminRoleChangeService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.user.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.user.entity.User; 5 | import mpersand.Gmuwiki.domain.user.enums.Role; 6 | import mpersand.Gmuwiki.domain.user.exception.UserNotFoundException; 7 | import mpersand.Gmuwiki.domain.user.presentation.dto.request.RoleChangeRequest; 8 | import mpersand.Gmuwiki.domain.user.repository.UserRepository; 9 | import mpersand.Gmuwiki.global.annotation.RollbackService; 10 | 11 | @RollbackService 12 | @RequiredArgsConstructor 13 | public class AdminRoleChangeService { 14 | 15 | private final UserRepository userRepository; 16 | 17 | public void execute(RoleChangeRequest roleChangeRequest) { 18 | 19 | String email = roleChangeRequest.getEmail(); 20 | 21 | User user = userRepository.findByEmail(email) 22 | .orElseThrow(() -> new UserNotFoundException()); 23 | 24 | user.updateRole(Role.ROLE_ADMIN); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/domain/user/service/UserRoleChangeService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.domain.user.service; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.user.entity.User; 5 | import mpersand.Gmuwiki.domain.user.enums.Role; 6 | import mpersand.Gmuwiki.domain.user.exception.UserNotFoundException; 7 | import mpersand.Gmuwiki.domain.user.presentation.dto.request.RoleChangeRequest; 8 | import mpersand.Gmuwiki.domain.user.repository.UserRepository; 9 | import mpersand.Gmuwiki.global.annotation.RollbackService; 10 | 11 | @RollbackService 12 | @RequiredArgsConstructor 13 | public class UserRoleChangeService { 14 | 15 | private final UserRepository userRepository; 16 | 17 | public void execute(RoleChangeRequest roleChangeRequest) { 18 | 19 | String email = roleChangeRequest.getEmail(); 20 | 21 | User user = userRepository.findByEmail(email) 22 | .orElseThrow(() -> new UserNotFoundException()); 23 | 24 | user.updateRole(Role.ROLE_STUDENT); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/annotation/ReadOnlyService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.annotation; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.springframework.transaction.annotation.Transactional; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Service 12 | @Transactional(readOnly = true) 13 | @Target(ElementType.TYPE) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface ReadOnlyService { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/annotation/RestRequestService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.annotation; 2 | 3 | import org.springframework.core.annotation.AliasFor; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | import java.lang.annotation.ElementType; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | @RestController 13 | @RequestMapping 14 | @Target(ElementType.TYPE) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface RestRequestService { 17 | 18 | @AliasFor(annotation = RequestMapping.class) 19 | String value() default ""; 20 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/annotation/RollbackService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.annotation; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.springframework.transaction.annotation.Transactional; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Service 12 | @Transactional(rollbackFor = Exception.class) 13 | @Target(ElementType.TYPE) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface RollbackService { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/config/querydsl/QuerydslConfig.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.config.querydsl; 2 | 3 | import com.querydsl.jpa.impl.JPAQueryFactory; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | import javax.persistence.EntityManager; 8 | import javax.persistence.PersistenceContext; 9 | 10 | @Configuration 11 | public class QuerydslConfig { 12 | 13 | @PersistenceContext 14 | private EntityManager entityManager; 15 | 16 | @Bean 17 | public JPAQueryFactory jpaQueryFactory() { 18 | return new JPAQueryFactory(entityManager); 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/config/s3/S3Config.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.config.s3; 2 | 3 | import com.amazonaws.auth.AWSCredentials; 4 | import com.amazonaws.auth.AWSStaticCredentialsProvider; 5 | import com.amazonaws.auth.BasicAWSCredentials; 6 | import com.amazonaws.services.s3.AmazonS3; 7 | import com.amazonaws.services.s3.AmazonS3ClientBuilder; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | public class S3Config { 14 | 15 | @Value("${cloud.aws.credentials.access-key}") 16 | private String accessKey; 17 | 18 | @Value("${cloud.aws.credentials.secret-key}") 19 | private String secretKey; 20 | 21 | @Value("${cloud.aws.region.static}") 22 | private String region; 23 | 24 | @Bean 25 | public AmazonS3 amazonS3() { 26 | 27 | AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey); 28 | 29 | return AmazonS3ClientBuilder.standard() 30 | .withRegion(region) 31 | .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) 32 | .build(); 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/error/ErrorCode.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.error; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | @AllArgsConstructor 8 | public enum ErrorCode { 9 | 10 | //SERVER ERROR 11 | UNKNOWN_ERROR("알 수 없는 에러입니다.", 500), 12 | EMAIL_SEND_FAIL("메일 발송에 실패했습니다", 500), 13 | 14 | //TOKEN 15 | TOKEN_IS_EXPIRED("토큰이 만료 되었습니다.", 401), 16 | TOKEN_NOT_VALID("토큰이 유효 하지 않습니다.", 401), 17 | 18 | //BOARD 19 | BOARD_NOT_FOUND("게시글을 찾을 수 없습니다.", 404), 20 | MISMATCH_BOARD_AUTHOR("내가 작성한 글이 아닙니다.", 403), 21 | ALREADY_EXIST_TITLE("이미 존재하는 제목입니다.", 409), 22 | BOARD_NOT_CHANGE("달라진 수정사항이 없습니다.", 400), 23 | 24 | //BOARD RECORD 25 | BOARD_RECORD_NOT_FOUND("게시글의 기록을 찾을 수 없습니다.", 404), 26 | 27 | //NOTICE 28 | NOTICE_NOT_FOUND("공지글을 찾을 수 없습니다", 404), 29 | 30 | //FILE 31 | NOT_ALLOWED_FILE("허용되지 않은 파일 형식입니다.", 400), 32 | FILE_UPLOAD_FAIL("파일 업로드에 실패했습니다.", 500), 33 | INVALID_FORMAT_FILE("잘못된 형식의 파일입니다.", 400), 34 | 35 | //USER 36 | USER_NOT_FOUND("사용자를 찾을 수 없습니다", 404), 37 | ROLE_NOT_EXIST("역할이 존재하지 않습니다.", 404), 38 | 39 | //INQUIRY 40 | INQUIRY_NOT_FOUND("문의 사항을 찾을 수 없습니다.", 404); 41 | 42 | private final String message; 43 | private final int status; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/error/ErrorMessage.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.error; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | @AllArgsConstructor 8 | public class ErrorMessage { 9 | private final String message; 10 | private final int status; 11 | 12 | public ErrorMessage(ErrorCode errorCode){ 13 | this(errorCode.getMessage(), errorCode.getStatus()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/error/GmuwikiException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.error; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class GmuwikiException extends RuntimeException { 7 | 8 | private ErrorCode errorCode; 9 | 10 | public GmuwikiException(ErrorCode errorCode) { 11 | super(errorCode.getMessage()); 12 | this.errorCode = errorCode; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/error/handler/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.error.handler; 2 | 3 | import mpersand.Gmuwiki.global.error.ErrorMessage; 4 | import mpersand.Gmuwiki.global.error.GmuwikiException; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.MethodArgumentNotValidException; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.RestControllerAdvice; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | 15 | @RestControllerAdvice 16 | public class GlobalExceptionHandler { 17 | 18 | private final Logger log = LoggerFactory.getLogger(this.getClass().getSimpleName()); 19 | 20 | @ExceptionHandler(GmuwikiException.class) 21 | public ResponseEntity handleComprehensiveException(HttpServletRequest request, GmuwikiException e) { 22 | printError(request, e.getErrorCode().getMessage()); 23 | ErrorMessage errorMessage = new ErrorMessage(e.getErrorCode().getMessage(), e.getErrorCode().getStatus()); 24 | return new ResponseEntity<>(errorMessage, HttpStatus.valueOf(e.getErrorCode().getStatus())); 25 | } 26 | 27 | @ExceptionHandler(MethodArgumentNotValidException.class) 28 | public ResponseEntity handleValidException(HttpServletRequest request, MethodArgumentNotValidException e) { 29 | printError(request, "요청값이 유효하지 않습니다."); 30 | ErrorMessage errorMessage = new ErrorMessage("요청값이 유효하지 않습니다.", HttpStatus.BAD_REQUEST.value()); 31 | return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST); 32 | } 33 | 34 | 35 | private void printError(HttpServletRequest request, String message) { 36 | log.error(request.getRequestURI()); 37 | log.error(message); 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/filter/ExceptionFilter.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.filter; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.jsonwebtoken.ExpiredJwtException; 5 | import io.jsonwebtoken.JwtException; 6 | import lombok.RequiredArgsConstructor; 7 | import mpersand.Gmuwiki.global.error.ErrorCode; 8 | import mpersand.Gmuwiki.global.error.ErrorMessage; 9 | import mpersand.Gmuwiki.global.error.GmuwikiException; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.stereotype.Component; 13 | import org.springframework.web.filter.OncePerRequestFilter; 14 | import javax.servlet.FilterChain; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.io.IOException; 18 | 19 | import static mpersand.Gmuwiki.global.error.ErrorCode.*; 20 | 21 | 22 | @Component 23 | @RequiredArgsConstructor 24 | public class ExceptionFilter extends OncePerRequestFilter { 25 | 26 | private final ObjectMapper objectMapper; 27 | 28 | private final Logger log = LoggerFactory.getLogger(getClass().getSimpleName()); 29 | 30 | public void setErrorResponse(ErrorCode errorCode, HttpServletResponse response) throws IOException { 31 | response.setStatus(errorCode.getStatus()); 32 | response.setContentType("application/json; charset=utf-8"); 33 | 34 | ErrorMessage errorMessage = new ErrorMessage(errorCode); 35 | 36 | String errorResponseEntityToJson = objectMapper.writeValueAsString(errorMessage); 37 | response.getWriter().write(errorResponseEntityToJson); 38 | } 39 | 40 | @Override 41 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException { 42 | try { 43 | filterChain.doFilter(request, response); 44 | } catch (ExpiredJwtException ex) { 45 | log.debug("================= [ ExceptionHandlerFilter ] 에서 ExpiredJwtException 발생 ==================="); 46 | setErrorResponse(TOKEN_IS_EXPIRED, response); 47 | } catch (JwtException | GmuwikiException ex) { 48 | log.debug("================= [ ExceptionHandlerFilter ] 에서 TokenNotValidException 발생 ==================="); 49 | setErrorResponse(TOKEN_NOT_VALID, response); 50 | } catch (Exception ex) { 51 | log.debug("================= [ ExceptionHandlerFilter ] 에서 Exception 발생 ==================="); 52 | setErrorResponse(UNKNOWN_ERROR, response); 53 | ex.printStackTrace(); 54 | } 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/filter/JwtRequestFilter.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.filter; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.global.security.jwt.TokenProvider; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.security.core.Authentication; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.stereotype.Component; 10 | import org.springframework.web.filter.OncePerRequestFilter; 11 | 12 | import javax.servlet.FilterChain; 13 | import javax.servlet.ServletException; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | 18 | @Component 19 | @RequiredArgsConstructor 20 | public class JwtRequestFilter extends OncePerRequestFilter { 21 | 22 | private final TokenProvider tokenProvider; 23 | 24 | private final Logger log = LoggerFactory.getLogger(getClass().getSimpleName()); 25 | 26 | @Override 27 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 28 | 29 | String token = tokenProvider.resolveToken(request); 30 | 31 | if(token != null && !token.isBlank()) { 32 | 33 | Authentication authentication = tokenProvider.authentication(token); 34 | 35 | SecurityContextHolder.getContext().setAuthentication(authentication); 36 | 37 | log.info("current user email = " + authentication.getName()); 38 | } 39 | 40 | filterChain.doFilter(request, response); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/gauth/GAuthConfig.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.gauth; 2 | 3 | import gauth.GAuth; 4 | import gauth.impl.GAuthImpl; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class GAuthConfig { 10 | 11 | @Bean 12 | public GAuth gauth() { 13 | return new GAuthImpl(); 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/gauth/properties/GAuthProperties.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.gauth.properties; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.boot.context.properties.ConstructorBinding; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | @ConstructorBinding 11 | @ConfigurationProperties(prefix = "gauth") 12 | public class GAuthProperties { 13 | 14 | private final String clientId; 15 | 16 | private final String clientSecret; 17 | 18 | private final String redirectUri; 19 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/logger/LoggingScheduler.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.logger; 2 | 3 | import com.amazonaws.services.s3.AmazonS3; 4 | import com.amazonaws.services.s3.model.CannedAccessControlList; 5 | import com.amazonaws.services.s3.model.ObjectMetadata; 6 | import com.amazonaws.services.s3.model.PutObjectRequest; 7 | import lombok.RequiredArgsConstructor; 8 | import mpersand.Gmuwiki.domain.file.exception.FileUploadFailedException; 9 | import mpersand.Gmuwiki.global.webhook.util.DiscordUtil; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.scheduling.annotation.Scheduled; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.io.File; 15 | import java.io.FileInputStream; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | @Component 22 | @RequiredArgsConstructor 23 | public class LoggingScheduler { 24 | 25 | private final AmazonS3 amazonS3; 26 | 27 | private final DiscordUtil discordUtil; 28 | 29 | @Value("${cloud.aws.s3.bucket}") 30 | private String bucket; 31 | 32 | @Value("${cloud.aws.s3.url}") 33 | private String url; 34 | 35 | @Scheduled(cron = "59 59 23 * * ?", zone = "Asia/Seoul") 36 | public void sendLog() { 37 | 38 | String logDir = "./src/main/resources/logs/"; 39 | 40 | File logDirectory = new File(logDir); 41 | 42 | List logUrlList = new ArrayList<>(); 43 | 44 | for(File file : logDirectory.listFiles()) { 45 | 46 | String fileName = file.getName(); 47 | 48 | ObjectMetadata objectMetadata = new ObjectMetadata(); 49 | 50 | objectMetadata.setContentLength(file.length()); 51 | 52 | objectMetadata.setContentType("text/plain"); 53 | 54 | try(InputStream inputStream = new FileInputStream(file)) { 55 | 56 | amazonS3.putObject( 57 | new PutObjectRequest(bucket, fileName, inputStream, objectMetadata) 58 | .withCannedAcl(CannedAccessControlList.PublicRead)); 59 | } catch (IOException e) { 60 | 61 | throw new FileUploadFailedException(); 62 | } 63 | 64 | file.delete(); 65 | 66 | logUrlList.add(url + fileName); 67 | } 68 | 69 | StringBuilder message = new StringBuilder("**로그 목록**\\n"); 70 | 71 | for (int i = 0; i < logUrlList.size(); i++) { 72 | message.append(i + 1).append(" ").append(logUrlList.get(i)).append("\\n"); 73 | } 74 | 75 | String singleDiscordMessage = discordUtil.toSingleDiscordMessage(message.toString().trim()); 76 | 77 | discordUtil.sendDiscordMessage(singleDiscordMessage); 78 | } 79 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/logger/filter/LogRequestFilter.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.logger.filter; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.web.filter.OncePerRequestFilter; 8 | 9 | import javax.servlet.FilterChain; 10 | import javax.servlet.ServletException; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.io.IOException; 14 | 15 | @Component 16 | @RequiredArgsConstructor 17 | public class LogRequestFilter extends OncePerRequestFilter { 18 | 19 | private final Logger log = LoggerFactory.getLogger(getClass().getSimpleName()); 20 | 21 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 22 | 23 | log.info("client ip = {}", request.getRemoteAddr()); 24 | 25 | log.info("request method = {}", request.getMethod()); 26 | 27 | log.info("request url = {}", request.getRequestURI()); 28 | 29 | log.info("client info = {}", request.getHeader("User-Agent")); 30 | 31 | filterChain.doFilter(request, response); 32 | 33 | log.info("response status = {}", response.getStatus()); 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/redis/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.redis; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.global.redis.properties.RedisProperties; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.data.redis.connection.RedisConnectionFactory; 9 | import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; 10 | 11 | @Configuration 12 | @RequiredArgsConstructor 13 | public class RedisConfig { 14 | 15 | private final RedisProperties redisProperties; 16 | 17 | @Bean 18 | @ConditionalOnMissingBean(RedisConnectionFactory.class) 19 | RedisConnectionFactory redisConnectionFactory() { 20 | return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort()); 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/redis/properties/RedisProperties.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.redis.properties; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.boot.context.properties.ConstructorBinding; 7 | 8 | @Getter 9 | @ConstructorBinding 10 | @RequiredArgsConstructor 11 | @ConfigurationProperties(prefix = "spring.redis") 12 | public class RedisProperties { 13 | 14 | private final String host; 15 | 16 | private final int port; 17 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/auth/AuthDetails.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.auth; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.user.entity.User; 5 | import org.springframework.security.core.GrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | 8 | import java.util.Collection; 9 | import java.util.Collections; 10 | 11 | 12 | @RequiredArgsConstructor 13 | public class AuthDetails implements UserDetails { 14 | 15 | private final User user; 16 | 17 | @Override 18 | public Collection getAuthorities() {return Collections.singleton(user.getRole()); } 19 | 20 | @Override 21 | public String getPassword() { 22 | return null; 23 | } 24 | 25 | @Override 26 | public String getUsername() { 27 | return user.getEmail(); 28 | } 29 | 30 | @Override 31 | public boolean isAccountNonExpired() { 32 | return true; 33 | } 34 | 35 | @Override 36 | public boolean isAccountNonLocked() { 37 | return true; 38 | } 39 | 40 | @Override 41 | public boolean isCredentialsNonExpired() { 42 | return true; 43 | } 44 | 45 | @Override 46 | public boolean isEnabled() { 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/auth/AuthDetailsService.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.auth; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.user.entity.User; 5 | import mpersand.Gmuwiki.domain.user.repository.UserRepository; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | @RequiredArgsConstructor 13 | public class AuthDetailsService implements UserDetailsService { 14 | 15 | private final UserRepository userRepository; 16 | 17 | @Override 18 | public UserDetails loadUserByUsername(String userName) { 19 | User user = userRepository.findByEmail(userName) 20 | .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); 21 | return new AuthDetails(user); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.config; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.global.filter.ExceptionFilter; 5 | import mpersand.Gmuwiki.global.filter.JwtRequestFilter; 6 | import mpersand.Gmuwiki.global.logger.filter.LogRequestFilter; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.http.HttpMethod; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 12 | import org.springframework.security.config.http.SessionCreationPolicy; 13 | import org.springframework.security.web.SecurityFilterChain; 14 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 15 | import org.springframework.web.cors.CorsUtils; 16 | 17 | @Configuration 18 | @RequiredArgsConstructor 19 | @EnableWebSecurity 20 | public class SecurityConfig { 21 | 22 | private final JwtRequestFilter jwtRequestFilter; 23 | 24 | private final ExceptionFilter exceptionFilter; 25 | 26 | private final LogRequestFilter logRequestFilter; 27 | 28 | @Bean 29 | protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { 30 | http 31 | .formLogin().disable() 32 | .httpBasic().disable() 33 | .cors().disable() 34 | .csrf().disable() 35 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 36 | .and() 37 | .authorizeRequests() 38 | .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() 39 | 40 | .antMatchers(HttpMethod.POST, "/auth").permitAll() 41 | .antMatchers(HttpMethod.PATCH, "/auth").permitAll() 42 | .antMatchers(HttpMethod.DELETE, "/auth").authenticated() 43 | 44 | .antMatchers("/file/**").authenticated() 45 | 46 | .antMatchers(HttpMethod.POST, "/board").authenticated() 47 | .antMatchers(HttpMethod.GET, "/board/search").authenticated() 48 | .antMatchers(HttpMethod.GET, "/board/recent").authenticated() 49 | .antMatchers(HttpMethod.GET, "/board").authenticated() 50 | .antMatchers(HttpMethod.GET, "/board/{id}/record").authenticated() 51 | .antMatchers(HttpMethod.GET, "/board/{id}/record/detail").authenticated() 52 | .antMatchers(HttpMethod.GET, "/board/{id}").authenticated() 53 | .antMatchers(HttpMethod.DELETE, "/board/{id}").authenticated() 54 | .antMatchers(HttpMethod.PATCH, "/board/{id}").authenticated() 55 | .antMatchers(HttpMethod.DELETE, "/board/{id}/record").hasAuthority("ROLE_ADMIN") 56 | 57 | .antMatchers(HttpMethod.POST, "/inquiry").authenticated() 58 | .antMatchers(HttpMethod.GET, "/inquiry").hasAuthority("ROLE_ADMIN") 59 | .antMatchers(HttpMethod.GET, "/inquiry/{id}").hasAuthority("ROLE_ADMIN") 60 | .antMatchers(HttpMethod.POST, "/inquiry/approve/{id}").hasAuthority("ROLE_ADMIN") 61 | .antMatchers(HttpMethod.POST, "/inquiry/refusal/{id}").hasAuthority("ROLE_ADMIN") 62 | 63 | .antMatchers(HttpMethod.GET, "/notice").authenticated() 64 | .antMatchers(HttpMethod.GET, "/notice/{id}").authenticated() 65 | .antMatchers(HttpMethod.POST, "/notice").hasAuthority("ROLE_ADMIN") 66 | .antMatchers(HttpMethod.PATCH, "/notice/{id}").hasAuthority("ROLE_ADMIN") 67 | .antMatchers(HttpMethod.DELETE, "/notice/{id}").hasAuthority("ROLE_ADMIN") 68 | 69 | .antMatchers("/role/**").hasAuthority("ROLE_ADMIN") 70 | 71 | .anyRequest().denyAll(); 72 | 73 | http 74 | .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class) 75 | .addFilterBefore(exceptionFilter, JwtRequestFilter.class) 76 | .addFilterBefore(logRequestFilter, ExceptionFilter.class); 77 | 78 | return http.build(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/exception/RefreshTokenExpirationException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class RefreshTokenExpirationException extends GmuwikiException { 9 | 10 | public RefreshTokenExpirationException() { 11 | super(ErrorCode.TOKEN_IS_EXPIRED); 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/exception/TokenExpirationException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class TokenExpirationException extends GmuwikiException { 9 | 10 | public TokenExpirationException() { 11 | super(ErrorCode.TOKEN_IS_EXPIRED); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/exception/TokenNotValidException.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.exception; 2 | 3 | import lombok.Getter; 4 | import mpersand.Gmuwiki.global.error.ErrorCode; 5 | import mpersand.Gmuwiki.global.error.GmuwikiException; 6 | 7 | @Getter 8 | public class TokenNotValidException extends GmuwikiException { 9 | 10 | public TokenNotValidException() { 11 | super(ErrorCode.TOKEN_NOT_VALID); 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/handler/CustomAccessDeniedHandler.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.handler; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.security.access.AccessDeniedException; 5 | import org.springframework.security.web.access.AccessDeniedHandler; 6 | import org.springframework.stereotype.Component; 7 | 8 | import javax.servlet.ServletException; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | 13 | @Component 14 | @Slf4j 15 | public class CustomAccessDeniedHandler implements AccessDeniedHandler { 16 | 17 | @Override 18 | public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { 19 | log.error("===Access Denied==="); 20 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/handler/CustomAuthenticationEntryPointHandler.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.handler; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.security.core.AuthenticationException; 5 | import org.springframework.security.web.AuthenticationEntryPoint; 6 | import org.springframework.stereotype.Component; 7 | 8 | import javax.servlet.ServletException; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | 13 | @Component 14 | @Slf4j 15 | public class CustomAuthenticationEntryPointHandler implements AuthenticationEntryPoint { 16 | 17 | @Override 18 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { 19 | log.info("===AuthenticationEntryPoint==="); 20 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/jwt/TokenProvider.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.jwt; 2 | 3 | import io.jsonwebtoken.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.RequiredArgsConstructor; 7 | import mpersand.Gmuwiki.domain.auth.exception.RoleNotExistException; 8 | import mpersand.Gmuwiki.domain.user.enums.Role; 9 | import mpersand.Gmuwiki.global.security.auth.AuthDetailsService; 10 | import mpersand.Gmuwiki.global.security.exception.TokenExpirationException; 11 | import mpersand.Gmuwiki.global.security.exception.TokenNotValidException; 12 | import mpersand.Gmuwiki.global.security.jwt.properties.JwtProperties; 13 | import mpersand.Gmuwiki.global.security.jwt.properties.TokenTimeProperties; 14 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 15 | import org.springframework.security.core.Authentication; 16 | import org.springframework.security.core.userdetails.UserDetails; 17 | import org.springframework.stereotype.Component; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | import java.security.Key; 21 | import java.time.ZonedDateTime; 22 | import java.util.Date; 23 | 24 | 25 | @Getter 26 | @Component 27 | @RequiredArgsConstructor 28 | public class TokenProvider { 29 | 30 | private final JwtProperties jwtProperties; 31 | 32 | private final TokenTimeProperties tokenTimeProperties; 33 | 34 | private final AuthDetailsService authDetailsService; 35 | 36 | @AllArgsConstructor 37 | private enum TokenObject { 38 | ACCESS_TYPE("access"), 39 | REFRESH_TYPE("refresh"), 40 | TOKEN_PREFIX("Bearer "), 41 | AUTHORITY("authority"); 42 | String value; 43 | } 44 | 45 | public ZonedDateTime accessExpiredTime() { 46 | 47 | return ZonedDateTime.now().plusSeconds(tokenTimeProperties.getAccessTime()); 48 | } 49 | 50 | public ZonedDateTime refreshExpiredTime() { 51 | 52 | return ZonedDateTime.now().plusSeconds(tokenTimeProperties.getRefreshTime()); 53 | } 54 | 55 | public String generateAccessToken(String email, Role role) { 56 | 57 | return generateToken(email, TokenObject.ACCESS_TYPE.value, jwtProperties.getAccessSecret(), tokenTimeProperties.getAccessTime(), role); 58 | } 59 | 60 | public String generateRefreshToken(String email, Role role) { 61 | 62 | return generateToken(email, TokenObject.REFRESH_TYPE.value, jwtProperties.getRefreshSecret(), tokenTimeProperties.getRefreshTime(), role); 63 | } 64 | 65 | public Authentication authentication(String token) { 66 | 67 | UserDetails userDetails = authDetailsService.loadUserByUsername(getTokenSubject(token, jwtProperties.getAccessSecret())); 68 | 69 | return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); 70 | } 71 | 72 | public String resolveToken(HttpServletRequest request) { 73 | 74 | String token = request.getHeader("Authorization"); 75 | 76 | if(token == null) { 77 | return null; 78 | } 79 | 80 | return parseToken(token); 81 | } 82 | 83 | public String exactEmailFromRefreshToken(String refresh) { 84 | return getTokenSubject(refresh, jwtProperties.getRefreshSecret()); 85 | } 86 | 87 | public Role exactRoleFromRefreshToken(String refresh) { 88 | 89 | String authority = getTokenBody(refresh, jwtProperties.getRefreshSecret()).get(TokenObject.AUTHORITY.value, String.class); 90 | 91 | switch (authority) { 92 | 93 | case "ROLE_STUDENT": 94 | return Role.ROLE_STUDENT; 95 | 96 | case "ROLE_ADMIN": 97 | return Role.ROLE_ADMIN; 98 | 99 | case "ROLE_GRADUATE": 100 | return Role.ROLE_GRADUATE; 101 | 102 | default: 103 | throw new RoleNotExistException(); 104 | } 105 | } 106 | 107 | public String generateToken(String email, String type, Key secret, Long exp, Role role) { 108 | 109 | final Claims claims = Jwts.claims().setSubject(email); 110 | 111 | claims.put("type", type); 112 | 113 | claims.put(TokenObject.AUTHORITY.value, role); 114 | 115 | return Jwts.builder() 116 | .setHeaderParam("typ", "JWT") 117 | .signWith(secret, SignatureAlgorithm.HS256) 118 | .setClaims(claims) 119 | .setIssuedAt(new Date()) 120 | .setExpiration(new Date(System.currentTimeMillis() + exp * 1000)) 121 | .compact(); 122 | } 123 | 124 | public String parseToken(String token) { 125 | 126 | if(token.startsWith(TokenObject.TOKEN_PREFIX.value)) { 127 | 128 | return token.replace(TokenObject.TOKEN_PREFIX.value, ""); 129 | 130 | } else { 131 | 132 | return null; 133 | } 134 | } 135 | 136 | private Claims getTokenBody(String token, Key secret) { 137 | 138 | try { 139 | 140 | return Jwts.parserBuilder() 141 | .setSigningKey(secret) 142 | .build() 143 | .parseClaimsJws(token) 144 | .getBody(); 145 | 146 | } catch (ExpiredJwtException e) { 147 | 148 | throw new TokenExpirationException(); 149 | 150 | } catch (JwtException e) { 151 | 152 | throw new TokenNotValidException(); 153 | } 154 | } 155 | 156 | private String getTokenSubject(String token, Key secret) { 157 | 158 | return getTokenBody(token, secret).getSubject(); 159 | } 160 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/jwt/properties/JwtProperties.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.jwt.properties; 2 | 3 | import io.jsonwebtoken.security.Keys; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.boot.context.properties.ConstructorBinding; 6 | 7 | import java.security.Key; 8 | 9 | @ConfigurationProperties(prefix = "jwt") 10 | @ConstructorBinding 11 | public class JwtProperties { 12 | 13 | private final Key accessSecret; 14 | private final Key refreshSecret; 15 | 16 | public JwtProperties(String accessSecret, String refreshSecret) { 17 | this.accessSecret = Keys.hmacShaKeyFor(accessSecret.getBytes()); 18 | this.refreshSecret = Keys.hmacShaKeyFor(refreshSecret.getBytes()); 19 | } 20 | 21 | public Key getAccessSecret() { 22 | return accessSecret; 23 | } 24 | 25 | public Key getRefreshSecret() { 26 | return refreshSecret; 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/security/jwt/properties/TokenTimeProperties.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.security.jwt.properties; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.boot.context.properties.ConstructorBinding; 7 | 8 | @Getter 9 | @AllArgsConstructor 10 | @ConstructorBinding 11 | @ConfigurationProperties(prefix = "jwt") 12 | public class TokenTimeProperties { 13 | 14 | private Long accessTime; 15 | 16 | private Long refreshTime; 17 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/util/BoardRecordUtil.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.util; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.BoardRecord; 5 | import mpersand.Gmuwiki.domain.board.exception.BoardRecordNotFoundException; 6 | import mpersand.Gmuwiki.domain.board.repository.BoardRecordRepository; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @RequiredArgsConstructor 11 | public class BoardRecordUtil { 12 | 13 | public final BoardRecordRepository boardRecordRepository; 14 | 15 | public BoardRecord findBoardRecordById(Long id) { 16 | 17 | return boardRecordRepository.findById(id) 18 | .orElseThrow(BoardRecordNotFoundException::new); 19 | } 20 | 21 | public void deleteBoardRecord(BoardRecord boardRecord) { 22 | 23 | boardRecordRepository.delete(boardRecord); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/util/BoardUtil.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.util; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.board.entity.Board; 5 | import mpersand.Gmuwiki.domain.board.exception.BoardNotFoundException; 6 | import mpersand.Gmuwiki.domain.board.repository.BoardRepository; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @RequiredArgsConstructor 11 | public class BoardUtil { 12 | 13 | private final BoardRepository boardRepository; 14 | 15 | public Board findBoardById(Long id) { 16 | 17 | return boardRepository.findById(id) 18 | .orElseThrow(() -> new BoardNotFoundException()); 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/util/NoticeUtil.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.util; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.notice.entity.Notice; 5 | import mpersand.Gmuwiki.domain.notice.exception.NoticeNotFoundException; 6 | import mpersand.Gmuwiki.domain.notice.repository.NoticeRepository; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @RequiredArgsConstructor 11 | public class NoticeUtil { 12 | 13 | private final NoticeRepository noticeRepository; 14 | 15 | public Notice findNoticeById(Long id) { 16 | 17 | return noticeRepository.findById(id) 18 | .orElseThrow(() -> new NoticeNotFoundException()); 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/util/UserUtil.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.util; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import mpersand.Gmuwiki.domain.user.exception.UserNotFoundException; 5 | import mpersand.Gmuwiki.domain.user.entity.User; 6 | import mpersand.Gmuwiki.domain.user.repository.UserRepository; 7 | import org.springframework.security.core.context.SecurityContextHolder; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | @RequiredArgsConstructor 12 | public class UserUtil { 13 | 14 | private final UserRepository userRepository; 15 | 16 | public User currentUser() { 17 | 18 | String email = SecurityContextHolder.getContext().getAuthentication().getName(); 19 | return userRepository.findByEmail(email) 20 | .orElseThrow(() -> new UserNotFoundException()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/web/WebConfig.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.web; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfig implements WebMvcConfigurer { 9 | 10 | @Override 11 | public void addCorsMappings(CorsRegistry corsRegistry) { 12 | 13 | corsRegistry.addMapping("/**") 14 | .allowedOrigins("http://localhost:3000", "https://gmuwiki.vercel.app", "https://gumwiki-dev.vercel.app") 15 | .allowedMethods("*") 16 | .allowedHeaders("*") 17 | .allowCredentials(true) 18 | .maxAge(3600); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/webhook/config/WebhookConfig.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.webhook.config; 2 | 3 | import okhttp3.OkHttpClient; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class WebhookConfig { 9 | 10 | private final OkHttpClient httpClient = new OkHttpClient(); 11 | 12 | @Bean 13 | public OkHttpClient httpClient() { 14 | return httpClient; 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /src/main/java/mpersand/Gmuwiki/global/webhook/util/DiscordUtil.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki.global.webhook.util; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import okhttp3.MediaType; 5 | import okhttp3.OkHttpClient; 6 | import okhttp3.Request; 7 | import okhttp3.RequestBody; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.stereotype.Component; 12 | 13 | import java.io.IOException; 14 | 15 | @Component 16 | @RequiredArgsConstructor 17 | public class DiscordUtil { 18 | 19 | private final OkHttpClient httpClient; 20 | 21 | @Value("${discord.webhook.url}") 22 | private String discordWebhookUrl; 23 | 24 | private final Logger log = LoggerFactory.getLogger(this.getClass().getSimpleName()); 25 | 26 | public void sendDiscordMessage(String message) { 27 | 28 | RequestBody requestBody = RequestBody.create(message, MediaType.parse("application/json; charset=utf-8")); 29 | 30 | Request request = new Request.Builder() 31 | .url(discordWebhookUrl) 32 | .post(requestBody) 33 | .build(); 34 | 35 | try(okhttp3.Response response = httpClient.newCall(request).execute()) { 36 | 37 | if(response.isSuccessful()) { 38 | 39 | log.trace("문의봇이 문의사항 요청을 성공적으로 전달했어요!"); 40 | } else { 41 | log.error(response.body().string()); 42 | log.error("문의봇이 문의사항 요청 대신 " + response.code() + " 코드를 보냈어요!"); 43 | } 44 | } catch (IOException e) { 45 | throw new RuntimeException(); 46 | } 47 | } 48 | 49 | public String toSingleDiscordMessage(String string) { 50 | 51 | return "{\n" + 52 | " \"content\":\"" + string + "\"\n" + 53 | "}".trim(); 54 | } 55 | 56 | public String createInquiryMessage(String inquiryName, String inquiryType) { 57 | 58 | return "{\n" + 59 | " \"content\": \"문의사항 요청이 들어왔어요!\",\n" + 60 | " \"embeds\": [\n" + 61 | " {\n" + 62 | " \"title\": \"문의사항을 확인해주세요!\",\n" + 63 | " \"color\": 5725911,\n" + 64 | " \"fields\": [\n" + 65 | " {\n" + 66 | " \"name\": \"문의사항 제목\",\n" + 67 | " \"value\": \"" + inquiryName + "\",\n" + 68 | " \"inline\": true\n" + 69 | " },\n" + 70 | " {\n" + 71 | " \"name\": \"문의사항 카테고리\",\n" + 72 | " \"value\": \"" + inquiryType + "\",\n" + 73 | " \"inline\": true\n" + 74 | " }\n" + 75 | " ]\n" + 76 | " }\n" + 77 | " ],\n" + 78 | " \"attachments\": []\n" + 79 | "}".trim(); 80 | } 81 | } -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | servlet: 3 | multipart: 4 | max-file-size: 20MB 5 | max-request-size: 25MB 6 | enabled: true 7 | 8 | datasource: 9 | driver-class-name: org.mariadb.jdbc.Driver 10 | username: ${DB_USER} 11 | url: ${DB_URL} 12 | password: ${DB_PASSWORD} 13 | 14 | mvc: 15 | pathmatch: 16 | matching-strategy: ant_path_matcher 17 | 18 | mail: 19 | host: ${MAIL_HOST} 20 | port: ${MAIL_PORT} 21 | username: ${MAIL_USER} 22 | password: ${MAIL_PASSWORD} 23 | properties: 24 | mail: 25 | smtp: 26 | auth: 27 | starttls: 28 | enable: true 29 | 30 | jpa: 31 | hibernate: 32 | ddl-auto: ${DDL_AUTO_TYPE} 33 | generate-ddl: true 34 | 35 | main: 36 | allow-bean-definition-overriding: 'true' 37 | 38 | redis: 39 | host: ${REDIS_HOST} 40 | port: ${REDIS_PORT} 41 | 42 | jwt: 43 | accessSecret: ${ACCESS_SECRET} 44 | refreshSecret: ${REFRESH_SECRET} 45 | time: 46 | accessTime: ${JWT_ACCESS_TIME} 47 | refreshTime: ${JWT_REFRESH_TIME} 48 | 49 | cloud: 50 | aws: 51 | region: 52 | static: ${AWS_REGION} 53 | credentials: 54 | access-key: ${AWS_ACCESS} 55 | secret-key: ${AWS_SECRET} 56 | s3: 57 | bucket: ${AWS_BUCKET_NAME} 58 | url: ${AWS_URL} 59 | stack: 60 | auto: false 61 | 62 | logging: 63 | level: 64 | com: 65 | amazonaws: 66 | util: 67 | EC2MetadataUtils: error 68 | 69 | gauth: 70 | clientId: ${CLIENT_ID} 71 | clientSecret: ${CLIENT_SECRET} 72 | redirectUri: ${REDIRECT_URI} 73 | 74 | discord: 75 | webhook: 76 | url: ${WEBHOOK_URL} -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | src/main/resources/logs/access-%d{yyyy-MM-dd}.%i.log 10 | 1GB 11 | 30 12 | 13 | 14 | utf8 15 | ${FILE_LOG_PATTERN} 16 | 17 | 18 | 19 | 20 | 21 | src/main/resources/logs/error-%d{yyyy-MM-dd}.%i.log 22 | 1GB 23 | 30 24 | 25 | 26 | utf8 27 | ${FILE_LOG_PATTERN} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/test/java/mpersand/Gmuwiki/GMuwikiApplicationTests.java: -------------------------------------------------------------------------------- 1 | package mpersand.Gmuwiki; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest(classes = GMuwikiApplicationTests.class) 7 | class GMuwikiApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------