From 9f1462ac3227cd9a58a2b78b8d3892bd6ca2099e Mon Sep 17 00:00:00 2001 From: FhRh Date: Mon, 9 Dec 2024 18:11:15 +0900 Subject: [PATCH 1/8] =?UTF-8?q?style(RequestLoggingInterceptor)=20:=20?= =?UTF-8?q?=EB=9D=84=EC=96=B4=EC=93=B0=EA=B8=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/wap/wabi/common/RequestLoggingInterceptor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wabi/src/main/kotlin/com/wap/wabi/common/RequestLoggingInterceptor.kt b/wabi/src/main/kotlin/com/wap/wabi/common/RequestLoggingInterceptor.kt index ebef885..62b792d 100644 --- a/wabi/src/main/kotlin/com/wap/wabi/common/RequestLoggingInterceptor.kt +++ b/wabi/src/main/kotlin/com/wap/wabi/common/RequestLoggingInterceptor.kt @@ -12,7 +12,7 @@ class RequestLoggingInterceptor : HandlerInterceptor { private val logger = LoggerFactory.getLogger(RequestLoggingInterceptor::class.java) override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { - logger.info("Request received from" + request.remoteAddr + "to" + request.requestURI) + logger.info("Request received from " + request.remoteAddr + " to" + request.requestURI) return true } } From cdcd1900ba5ea8dd2d4fd8d803fd00bd501610be Mon Sep 17 00:00:00 2001 From: FhRh Date: Tue, 10 Dec 2024 01:38:11 +0900 Subject: [PATCH 2/8] =?UTF-8?q?chore(build.gradle.kts)=20:=20=EC=95=A1?= =?UTF-8?q?=EC=B8=84=EC=97=90=EC=9D=B4=ED=84=B0,=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EB=A9=94=ED=85=8C=EC=9A=B0=EC=8A=A4=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wabi/build.gradle.kts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/wabi/build.gradle.kts b/wabi/build.gradle.kts index a015b35..74cedeb 100644 --- a/wabi/build.gradle.kts +++ b/wabi/build.gradle.kts @@ -21,6 +21,10 @@ repositories { } dependencies { + //Monitoring + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("io.micrometer:micrometer-registry-prometheus") + //QueryDSL implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta") implementation("com.querydsl:querydsl-apt:5.0.0:jakarta") @@ -40,10 +44,10 @@ dependencies { implementation("org.apache.poi:poi-ooxml:5.2.3") //jwt - implementation ("org.springframework.boot:spring-boot-starter-security") - implementation ("io.jsonwebtoken:jjwt-api:0.11.5") - implementation ("io.jsonwebtoken:jjwt-impl:0.11.5") - implementation ("io.jsonwebtoken:jjwt-jackson:0.11.5") + implementation("org.springframework.boot:spring-boot-starter-security") + implementation("io.jsonwebtoken:jjwt-api:0.11.5") + implementation("io.jsonwebtoken:jjwt-impl:0.11.5") + implementation("io.jsonwebtoken:jjwt-jackson:0.11.5") implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-web") From 0abbc7eb19e1283db4636d5d2d76c50e0cf66016 Mon Sep 17 00:00:00 2001 From: FhRh Date: Tue, 10 Dec 2024 01:38:48 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat(JwtAuthenticationFilter)=20:=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=EC=9D=B4=20=EC=97=86=EC=96=B4=EB=8F=84=20?= =?UTF-8?q?=EC=9D=BC=EB=8B=A8=20=EB=84=98=EA=B8=B0=EA=B3=A0,=20Security?= =?UTF-8?q?=EC=97=90=20=EC=9D=98=ED=95=B4=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wap/wabi/auth/jwt/JwtAuthenticationFilter.kt | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/wabi/src/main/kotlin/com/wap/wabi/auth/jwt/JwtAuthenticationFilter.kt b/wabi/src/main/kotlin/com/wap/wabi/auth/jwt/JwtAuthenticationFilter.kt index 4c2a498..0c0d5b5 100644 --- a/wabi/src/main/kotlin/com/wap/wabi/auth/jwt/JwtAuthenticationFilter.kt +++ b/wabi/src/main/kotlin/com/wap/wabi/auth/jwt/JwtAuthenticationFilter.kt @@ -24,18 +24,13 @@ class JwtAuthenticationFilter( response: HttpServletResponse, filterChain: FilterChain ) { - val path = request.requestURI - if (path.startsWith("/swagger-ui/") || path.startsWith("/v3") || path.startsWith("/auth")) { - filterChain.doFilter(request, response) - return - } - try { parseBearerToken(request, HttpHeaders.AUTHORIZATION)?.let { accessToken -> jwtTokenProvider.validateAndParseToken(accessToken) val user = parseUserSpecification(accessToken) - val authentication = UsernamePasswordAuthenticationToken.authenticated(user, accessToken, user.authorities) + val authentication = + UsernamePasswordAuthenticationToken.authenticated(user, accessToken, user.authorities) authentication.details = WebAuthenticationDetails(request) SecurityContextHolder.getContext().authentication = authentication } @@ -45,9 +40,7 @@ class JwtAuthenticationFilter( return // 새 토큰 발급 후 요청 종료 } } catch (e: Exception) { - response.status = HttpServletResponse.SC_UNAUTHORIZED - response.writer.write("Invalid Token") - return + logger.info("No Token") } filterChain.doFilter(request, response) From 5097fdd4e023c8594e1d167e5c7b070378cd59da Mon Sep 17 00:00:00 2001 From: FhRh Date: Tue, 10 Dec 2024 01:39:10 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat(SecurityConfig)=20:=20=EC=95=A1?= =?UTF-8?q?=EC=B8=84=EC=97=90=EC=9D=B4=ED=84=B0=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=ED=95=84=EC=9A=94=20x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/com/wap/wabi/auth/config/SecurityConfig.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wabi/src/main/kotlin/com/wap/wabi/auth/config/SecurityConfig.kt b/wabi/src/main/kotlin/com/wap/wabi/auth/config/SecurityConfig.kt index a1ab539..449420f 100644 --- a/wabi/src/main/kotlin/com/wap/wabi/auth/config/SecurityConfig.kt +++ b/wabi/src/main/kotlin/com/wap/wabi/auth/config/SecurityConfig.kt @@ -29,7 +29,8 @@ class SecurityConfig( .requestMatchers( "/swagger-ui/**", "/v3/api-docs/**", - "/health" + "/health", + "/actuator/**" ).permitAll() .anyRequest().authenticated() }.sessionManagement { session -> From 06b1a0966418b2530b897390fd9b153be0668443 Mon Sep 17 00:00:00 2001 From: FhRh Date: Tue, 10 Dec 2024 01:42:09 +0900 Subject: [PATCH 5/8] =?UTF-8?q?deploy(gradle.yml)=20:=20=EC=9D=B4=ED=9B=84?= =?UTF-8?q?=20=EC=9E=91=EC=97=85=EC=9D=84=20=EA=B0=9C=EB=B0=9C=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=ED=99=98=EA=B2=BD=EC=97=90=EC=84=9C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 35fd967..6bb5d32 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -2,7 +2,7 @@ name: Deploy on: push: - branches: [ "develop" ] + branches: [ "develop", "Feat/#53" ] pull_request: branches: [ "develop" ] From dd1a89eba40cb54c26b553f5c02ca13af2850a66 Mon Sep 17 00:00:00 2001 From: FhRh Date: Tue, 10 Dec 2024 02:01:03 +0900 Subject: [PATCH 6/8] =?UTF-8?q?chore(application.properties)=20:=20metric?= =?UTF-8?q?=20export=EA=B4=80=EB=A0=A8=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wabi/src/main/resources/application.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wabi/src/main/resources/application.properties b/wabi/src/main/resources/application.properties index 4f4a064..73444ea 100644 --- a/wabi/src/main/resources/application.properties +++ b/wabi/src/main/resources/application.properties @@ -1,2 +1,4 @@ spring.application.name=wabi spring.profiles.active=prod +management.endpoints.web.exposure.include=prometheus, health, info +management.metrics.tags.application=${spring.application.name} From 7e89339ed434db40fb6d0c20f4e6bdc60f789ab6 Mon Sep 17 00:00:00 2001 From: FhRh Date: Tue, 10 Dec 2024 21:53:09 +0900 Subject: [PATCH 7/8] =?UTF-8?q?feat(UserActivityManager)=20:=20DAU,=20MAU?= =?UTF-8?q?=EA=B3=84=EC=82=B0=20=EB=B0=8F=20export?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그인시 계정 없음에 대한 에러코드를 정의하였습니다. - 로그인시 하루 기준으로 중복되지 않는 사용자에 대해 카운트하고, metricRegistry를 통해 export 합니다. --- .../kotlin/com/wap/wabi/WabiApplication.kt | 2 + .../com/wap/wabi/auth/admin/entity/Admin.java | 13 ++++++ .../wabi/auth/admin/service/AdminService.kt | 11 ++++- .../auth/admin/util/UserActivityManager.kt | 42 +++++++++++++++++++ .../com/wap/wabi/exception/ErrorCode.kt | 1 + 5 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 wabi/src/main/kotlin/com/wap/wabi/auth/admin/util/UserActivityManager.kt diff --git a/wabi/src/main/kotlin/com/wap/wabi/WabiApplication.kt b/wabi/src/main/kotlin/com/wap/wabi/WabiApplication.kt index ef2759f..594ffd4 100644 --- a/wabi/src/main/kotlin/com/wap/wabi/WabiApplication.kt +++ b/wabi/src/main/kotlin/com/wap/wabi/WabiApplication.kt @@ -4,10 +4,12 @@ import jakarta.annotation.PostConstruct import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import org.springframework.scheduling.annotation.EnableScheduling import java.util.TimeZone @SpringBootApplication @EnableJpaAuditing +@EnableScheduling class WabiApplication { @PostConstruct fun started() { diff --git a/wabi/src/main/kotlin/com/wap/wabi/auth/admin/entity/Admin.java b/wabi/src/main/kotlin/com/wap/wabi/auth/admin/entity/Admin.java index d33a627..8cb665a 100644 --- a/wabi/src/main/kotlin/com/wap/wabi/auth/admin/entity/Admin.java +++ b/wabi/src/main/kotlin/com/wap/wabi/auth/admin/entity/Admin.java @@ -116,4 +116,17 @@ public Admin build() { return new Admin(this); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Admin admin = (Admin) o; + return id.equals(admin.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } } diff --git a/wabi/src/main/kotlin/com/wap/wabi/auth/admin/service/AdminService.kt b/wabi/src/main/kotlin/com/wap/wabi/auth/admin/service/AdminService.kt index c067aec..003ca74 100644 --- a/wabi/src/main/kotlin/com/wap/wabi/auth/admin/service/AdminService.kt +++ b/wabi/src/main/kotlin/com/wap/wabi/auth/admin/service/AdminService.kt @@ -7,6 +7,7 @@ import com.wap.wabi.auth.admin.payload.response.AdminLoginResponse import com.wap.wabi.auth.admin.repository.AdminRefreshTokenRepository import com.wap.wabi.auth.admin.repository.AdminRepository import com.wap.wabi.auth.admin.util.AdminValidator +import com.wap.wabi.auth.admin.util.UserActivityManager import com.wap.wabi.auth.jwt.JwtTokenProvider import com.wap.wabi.exception.ErrorCode import com.wap.wabi.exception.RestApiException @@ -20,7 +21,8 @@ class AdminService( private val adminRefreshTokenRepository: AdminRefreshTokenRepository, private val tokenProvider: JwtTokenProvider, private val adminValidator: AdminValidator, - private val encoder: PasswordEncoder + private val encoder: PasswordEncoder, + private val userActivityManager: UserActivityManager ) { @Transactional fun registerAdmin(adminRegisterRequest: AdminRegisterRequest) { @@ -36,7 +38,11 @@ class AdminService( adminValidator.validateLogin(adminLoginRequest) val admin = adminRepository.findByName(adminLoginRequest.name) ?.takeIf { admin -> - encoder.matches(adminLoginRequest.password, admin.get().password) + try { + encoder.matches(adminLoginRequest.password, admin.get().password) + } catch (e: Exception) { + throw RestApiException(ErrorCode.BAD_REQUEST_ADMIN_LOGIN) + } } ?: throw RestApiException(ErrorCode.BAD_REQUEST_NOT_EXIST_ADMIN) val refreshToken = tokenProvider.createRefreshToken() @@ -54,6 +60,7 @@ class AdminService( ) val accessToken = tokenProvider.createAccessToken("${admin.get().username}:${admin.get().role}") + userActivityManager.updateUserActivity(admin.get()) return AdminLoginResponse( name = admin.get().username, role = admin.get().role, diff --git a/wabi/src/main/kotlin/com/wap/wabi/auth/admin/util/UserActivityManager.kt b/wabi/src/main/kotlin/com/wap/wabi/auth/admin/util/UserActivityManager.kt new file mode 100644 index 0000000..ed45cba --- /dev/null +++ b/wabi/src/main/kotlin/com/wap/wabi/auth/admin/util/UserActivityManager.kt @@ -0,0 +1,42 @@ +package com.wap.wabi.auth.admin.util + +import com.wap.wabi.auth.admin.entity.Admin +import io.micrometer.core.instrument.Gauge +import io.micrometer.core.instrument.Metrics +import org.slf4j.LoggerFactory +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Component + + +@Component +class UserActivityManager { + private var dauCounter = 0 + private var mauCounter = 0 + + private val activeUsersToday = HashSet() + + private val logger = LoggerFactory.getLogger(UserActivityManager::class.java) + + fun updateUserActivity(admin: Admin) { + if (activeUsersToday.add(admin)) { + dauCounter++ + mauCounter++ + } + + Gauge.builder("user_activity_dau") { dauCounter }.register(Metrics.globalRegistry) + Gauge.builder("user_activity_mau") { mauCounter }.register(Metrics.globalRegistry) + } + + @Scheduled(cron = "0 0 0 * * ?") + fun resetDailyActiveUser() { + dauCounter = 0 + activeUsersToday.clear() + logger.info("Reset daily active user") + } + + @Scheduled(cron = "0 0 0 1 * ?") + fun resetMonthlyActiveUser() { + mauCounter = 0 + logger.info("Reset monthly active user") + } +} diff --git a/wabi/src/main/kotlin/com/wap/wabi/exception/ErrorCode.kt b/wabi/src/main/kotlin/com/wap/wabi/exception/ErrorCode.kt index 2a95c5a..1ae478c 100644 --- a/wabi/src/main/kotlin/com/wap/wabi/exception/ErrorCode.kt +++ b/wabi/src/main/kotlin/com/wap/wabi/exception/ErrorCode.kt @@ -45,4 +45,5 @@ enum class ErrorCode( BAD_REQUEST_ADMIN_NAME(HttpStatus.BAD_REQUEST, "800-3", "이름이 형식에 맞지 않습니다."), BAD_REQUEST_EXIST_ADMIN(HttpStatus.BAD_REQUEST, "800-4", "이미 존재하는 어드민입니다."), BAD_REQUEST_NOT_EXIST_ADMIN(HttpStatus.BAD_REQUEST, "800-5", "존재하지 않는 어드민입니다."), + BAD_REQUEST_ADMIN_LOGIN(HttpStatus.BAD_REQUEST, "800-6", "일치하는 계정이 없습니다."), } From 4ce0163050450ee5e5e4d6497ae3de1e5d84182c Mon Sep 17 00:00:00 2001 From: FhRh Date: Wed, 11 Dec 2024 00:28:06 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[Deploy]=20Feat/#53=EB=B8=8C=EB=9E=9C?= =?UTF-8?q?=EC=B9=98=20CI/CD=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 6bb5d32..35fd967 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -2,7 +2,7 @@ name: Deploy on: push: - branches: [ "develop", "Feat/#53" ] + branches: [ "develop" ] pull_request: branches: [ "develop" ]